12

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?

7
  • Which one makes more sense from a business perspective? Commented Aug 18, 2016 at 19:44
  • It's an artificial example. My question is about the interpretation of the two cited paragraphs from Evans book. But the artificial domain experts would talk about "increasing the quantity of an cart item". So cart, item and quantity are part of the UL. Commented Aug 18, 2016 at 19:47
  • 1
    Your example is no more artificial than Evans' edicts. It might be useful to point out that DDD is not actually a programming technique, though there are many people who try to use it that way. Commented Aug 18, 2016 at 19:48
  • Can you share a bit more text from the book, so that your disembodied quotes have more context? Commented Aug 18, 2016 at 19:58
  • 2
    Yes, thanks. What Evans is saying is that you should allow the Aggregate Root to control its own internal state (a good practice for any class, really). It therefore probably makes more sense to allow the cart to control quantity increases with cart.increaseItemQuantity(itemId), if for no other reason than it is less of a Law of Demeter violation. Calling cart.increaseItemQuantity(itemId) allows you to do things like update the cart's total amounts. Commented Aug 18, 2016 at 21:07

1 Answer 1

4

As long as Item cannot exist without Cart also being present, then there is no difference between the two options. It is possible to keep invariants in both cases.

In the case of method being on Item, the Item can "notify" it's parent cart to check the invariant when it needs to change it's own state. This makes things slightly more complicated, because then there is cyclical dependency between Item and Cart (which I assume is not a problem thanks to assumption in first sentence and something that IMO needs to exist either way).

In case of method being on Cart, it makes it simper, because there is no need for Item to reference Cart. But it makes it complicated because now the method not only checks for invariant and does the state change. But it also needs to ensure the item (or it's ID) is valid for this Cart. In the other case, this is already handled by method that queries given item from cart.

tl;dr; Both options have clear advantages and disadvantages and neither seems to be obviously better or worse than other.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.