I would like to write an application something like ecommerce.
And you know that in similar applications products could have different properties and features. To simulate such an opportunity I've created the following domain model entities:
Category – this is something like "electronics > сomputers" i.e types of products. Сategories contain a list of properties (List< Property >).
Property – independent entity that contains the name, units of measure, data type. For example "name", "weight", "screen size". The same property can have different products.
Product – just contains name and list of values relating to properties. Value is an object that contains just the value field and the field id of the property.
I originally decided to make the Category like single aggregate in this scheme because for example when I add new product I need to know all data related with current category including properties related to the current category (category.AddNewProduct(product)).
But what should I do when I just need to add a new property that does not belong to any category. For example, I can not do this category.AddNewProperty(property) because it clearly says that we add the property to specific category.
Ok the next step I decided separate Property into a separate aggregate but then it will be a list with simple entities.
Of course I can create something like PropertyAggregate to keep inside list of properties and buisness rules but when I add a product, I need to have inside the category the entire list of properties belonging to this category to check the invariants. But I'm also aware that keeping the links inside the aggregate on other aggregates is a bad practice.
What are the options to design this business case?
Best Answer
In the DDD perspective,
Category
,Product
andProperty
are entities: they all correspond to objects that have their own identity.Option 1: your original design
You made
Category
the root of a single aggregate. On one side, this makes sense, because the aggregate shall ensure consistency when its objects are modified, andProduct
must have theProperties
of itsCategory
:But on the other side, the single aggregate means that all its objects are related to a root that owns them, and all external references must be made via this aggregate root. This implies that:
Product
belongs to one and only oneCategory
. If theCategory
is deleted, so are itsProducts
.Property
belongs to one and only oneCategory
. Otherwise said, if "TV screens" and "Computer monitors" would be two categories, "TV screens:size" and "Computer monitors:size" would be two different properties.The second point doesn't correspond to your narrative: "But what should I do when I just need to add a new
Property
that does not belong to any category". And it's not clear if the sameProperties
can be used in differentCategories
.Option 2: Property outside the aggregate
If a
Property
exists independently of theCategories
, it must be outside the aggregate. And the same if you want to shareProperties
betweenCategories
(which makes sense for height, width, sizes, etc...). This seems definitively be the case.The consequence is on the link between
Property
and things that belong to the aggregate: while you can navigate from the inner of the aggregate toProperty
, you are no longer allowed to go directly from aProperty
to the corresponding values. This navigability restriction can be shown in an UML diagram:Note that this design doesn't prevent you to have a
List<Property>
inCategory
, with a reference semantic (e.g. java): each reference in the list refers to a sharableProperty
object in a repository.The only problem with this design is that you could change a
Property
or delete it: as it's outside of the aggregate, the aggregate can't take care of the consistency of its invariants. But this is not a problem. It is the consequence of the DDD principles and the complexity of real world. Here a quote from Eric Evans in his seminal book "Domain-Driven Design: Tackling Complexity in the Heart of Software":So yes, if you change a
Property
, you'll have to make sure that a service checks the Categories referring to it are updated as needed.Option 3: Category, Property and Product in different aggregates
I just wonder if the assumption that a
Product
belongs to a singleCategory
is founded:Product
under severalCategories
. For example, you'd find "Laptop Brand X Model Y" under the category "Laptops" and the category "Computers", and a "multifunction printer Z" under category "printer", "scanner" and "fax".Product
first, and only later assigns it to Categories and fill the values ?It won't simplify the aggregates, and you would have even more rules that span aggregates. But your system would be much more future proof.