I'm trying to develop my first application using Domain Driven Design rules and patterns.
In my business scenario I have to manage the list of Customers and to track all the package that are sent to a specific customer destination. So, following the rules, it turns out that:
- Customer is an Entity which is also an AggregateRoot.
- Destination is an Entity which is child of Customer (because it doesn’t make sense without a Customer)
- Package is an Entity. It is also another AggregateRoot because is a different transactional boundary I developed the Customer Aggregate with the method AddDestination(string destinationName …) that is responsable of creating a new Destination for the Customer.
Now I need to develop the Package class, where I have to maintain a reference to the destination where the package has been shipped. Following the DDD rules I can't reference the child entity id directly because I can’t reference entity that are child of another aggregate.
What do I have to do?
- Do I have to create a new ValueObejct with the required data of the Destination and expose it? (in this way I'm able to preserve the encapsulation and the rules of the Destionation which is accessible in writing only through Customer AggregateRoot). In this way external classes can access the fields of the Destination and perform logics/check using those but can't alternate the state of the destination.
- Do I have to add a DestinationNumber field (progressive within customer), create a ValueObject that contains CustomerId and DestinationNumber and use it in the Package aggregate?
- ?
Can someone please help me? Please elaborate the reply because I'd like to understand more this cases.
Aggregates in DDD are more about transaction boundaries rather than denoting if one entity's existence makes sense without the other.
Assumption A - Destination should rather be an aggregate
If it makes sense in your domain to modify Destination within it's own transaction without having to take a Customer in account then it might be you missed Destination as an aggregate on it's own.
This would also make sense if Destinations would be shared between customers or would be moved from one Customer to another (by identity reference of course).
Assumption B - Destination is definitely a child entity of Customer
If on the other hand only the customer aggregate can make sure that business invariants for customer's destinations can be met at all times, Destination should only be changed within the transaction boundaries of the customer and thus stay a child entity of customer.
In your Package aggregate you could of course only remember the Destinations id if you only need it for reading purpose, for instance.
But think this is not a good idea. On the hand not in including the Customer's id as well in the reference makes this relationship less explicit. And on the other hand if Destination is really an entity (and not a value object only) the id of Destination only needs to be unique in the domain within the boundaries of the corresponding customer.
So in case Destination is really a child entity of Customer I would go with your option 2 by introducing a reference value object such as, e.g. CustomerDestination including the customer id and whatever identifier is suitable to identify the destination within the scope of the specific customer.
Note: this does not have an influence on your database model where you might, depending on how you design it, still need some globally unique surrogate Id in the database.