In the DDD perspective,
Property are entities: they all correspond to objects that have their own identity.
Option 1: your original design
Category the root of a single aggregate. On one side, this makes sense, because the aggregate shall ensure consistency when its objects are modified, and
Product must have the
Properties of its
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:
- one specific
Product belongs to one and only one
Category. If the
Category is deleted, so are its
- a specific
Property belongs to one and only one
Category. 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 same
Properties can be used in different
Option 2: Property outside the aggregate
Property exists independently of the
Categories, it must be outside the aggregate. And the same if you want to share
Categories (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 to
Property, you are no longer allowed to go directly from a
Property 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
Category, with a reference semantic (e.g. java): each reference in the list refers to a sharable
Property 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":
Any rule that spans AGGREGATES will not be expected to be up-to-date
at all times. Through event processing, batch processing, or other
update mechanisms, other dependencies can be resolved within some
specified time. But the invariants applied within an AGGREGATE will be
enforced with the completion of each transaction.
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 single
Category is founded:
- I frequently see online shops proposing one
Product under several
Categories. 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".
- Isn't it possible that someone creates a
Product first, and only later assigns it to Categories and fill the values ?
- If you want to split a category, would you really delete its Products and then recreate them under the new categories ?
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.