Aggregate Roots with a collection of another AR

Wednesday, 30 September 2009 08:43 by ebenroux

I have been wondering whether an aggregate root should ever contain a collection of another aggregate root.  Back to the Customer.  Is it OK to have a collection of Orders?  One could argue that a customer may have many thousands of orders after some time so it would be impractical.  However, such a collection (in the domain) would mean that the customer only has a collection of the open orders.  So in a real sense this list should never be all that big.  Using CQS one may even remove the completed orders from the domain store.

Even so, what purpose would this collection serve.  It seems, to me anyway, that the only time such a collection of ARs should be considered is when it falls within the consistency boundary of the containing AR.  So, as per DDD, an aggregate root represents a consistency boundary.  For example, an order with its contained order items needs to be consistent; and it seems rather obvious.

Then one comes across issues such as: "A bronze customer may not have more than 2 open orders at any one time and with a total of no more than $10,000".  Similar rules apply to other customer types.  A typical reaction is to have a reference to the customer in an order to get the type (bronze, etc.) and to be able to ask the OrderRepository for the total of all open orders for the customer.  That is one way to do it, Iguess.

However, suddenly the consistency boundary has moved.  It is now around the customer.  This means that one would no longer add an order willy-nilly but rather add the order to the customer.  So now we need an Orders collection for the customer.  This does not mean that an Order is no longer an aggregate.  This is also why some people reckon that the aggregate root changes depending on the use case.  It is possible that when applying an 'apply order discount' task that the order stays the AR since it doesn't affect the overall collection of orders within the customer boundary.  It also does not mean that we will not have an OrderRepository.  But it does mean that when applying the CreateOrderCommand in our domain that the customer will be loaded, the newly created order will be added to the customer, the consistency checked and if OK the order will be persisted using the OrderRepository.

Comments

October 7. 2009 10:39

Tom

Do you have to shift an AR just to support a new requirement? Wouldn't that make it hard to implement new requirements?

My immediate thought was just to add an open order counter and an open order total to the customer AR, and update these via CQS. If Order AR's are always created by Customer (customer.Order() or something similar), you can check these counters in the Order() method, not allowing new Order AR's to be created until the rule is met.

I could be way off the mark, but that's why im posting - to find out..

Tom

October 7. 2009 10:59

ebenroux

Hi Tom,

What you are proposing is quite possible in some instances.  For values that are used for the query side (display / reporting) it is OK since, in most CQS situations anyway, the results are *eventually* consistent.  However, in the domain store we need this data to be consistent within the AR.  Using task-based interaction along with the relevant isolation level we can guarantee that reading the data gives us a consistent state.  Now it may be that updating the OrderTotal and NumberOfOpenOrders is always consistent in your example, in which case there is no problem with it.

As for requirements changing --- I guess that's what keeps us busy during the day Smile

ebenroux

Comments are closed