Domain Driven Design: reference between entities of different aggregates

1.9k Views Asked by At

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?

  1. 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.
  2. 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?
  3. ?

Can someone please help me? Please elaborate the reply because I'd like to understand more this cases.

2

There are 2 best solutions below

2
On

If I understand correctly, I would assume that this Package must be delivered somewhere, to a Destination.

I would also assume that Customer is indeed an Entity and also an Aggregate Root because it would have behaviours such as changing names, etc. Package is also an Entity, and also an Aggregate Root of itself, since it has different transaction boundary from Customer and also has some behavior (like addDestination).

Here, I don't see that Destination should be an Entity, because it does not have any behavior or identity of itself, rather it just contains a fact that this Destination refers to a Customer. So, I would model Destination as a Value Object instead, with an attribute of CustomerId (this can be done because Customer is an Aggregate Root).

Package's addDestination's argument would be a Value Object of Destination. If Package should ever have to change the destination, it can just create another instance of Destination and discard the old one.

Also, it does not make sense if Destination is a child of Customer, since there might be some Customers that are not delivered anything yet. Instead, I would model Destination as a child of Package, since it makes sense that every creation of Package, it should have a Destination (under the assumption that any Package must be delivered). And so Package and Destination are in the same aggregate.

4
On

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.