Evans introduces in his book "Domain Driven Design" in Chapter 6 "Aggregates" the concept of Aggregates. He further defines rules to translate that concept into an implementation (Evans 2009, pp. 128-129):
The root ENTITY can hand references to the internal ENTITIES to other objects, but those objects can use them only transiently, and they may not hold on to the reference.
After elaborating on other rules he summarizes them into this paragraph:
Cluster the Entities and Value Objects into Aggregates and define boundaries around each. Choose one Entity to be the root of each Aggregate, and control all access to the objects inside the boundary through the root. Allow external objects to hold references to the root only. Transient references to internal members can be passed out for use within a single operation only. Because the root controls access, it cannot be blindsided by changes to the internals. This arrangement makes it practical to enforce all invariants for objects in the Aggregate and for the Aggregate as a whole in any state change.
So what does transient usage exactly mean?
My colleague understands that only the aggregate root exposes a public interface for the clients. Clients will have no opportunity to call any operation on an entity other than the aggregate root.
My understanding of the cited sentences is different. I understand that it does indeed explicitly allow clients calling operations on internal entities. However only after getting them from the root.
So let's have a concrete example:
Let's say a Cart
consists of many Items
. Each Item
has a Quantity
. The model should support the use case "Increase the quantity of one specific Item". No invariants could be violated which affects anything outside of the Item.
Is a model violating above cited rules, when a client can do this by calling cart.item(itemId).increaseQuantity()
or should a client only be allowed to call a cart.increaseItemQuantity(itemId)
? What would be the benefit of the latter?
cart.increaseItemQuantity(itemId)
, if for no other reason than it is less of a Law of Demeter violation. Callingcart.increaseItemQuantity(itemId)
allows you to do things like update the cart's total amounts.