Search code examples
domain-driven-designaggregateroot

Effective aggregate root design in DDD (Vernon's explanations)


I have a question regarding the design of aggregates, as presented by Vernon, both in his articles in the DDD community (Effective Aggregate Design, Part 3) as well as on his book (Implementing Domain-Driven Design).

In there, he explores two possible approaches when designing BacklogItem and Task. In one of them Task is not its own Aggregate Root because it runs the "risk of leaving the true invariant unprotected", and the aggregate root is the BacklogItem

However, one of the other guidelines for designing aggregates roots is that access to an entity should only be done through the root itself. Which means that in order to get access to a Task in this approach one would have to now the BacklogItem it belongs to and ask for the backlog item. Normally, though one would just want to see the Tasks is assigned with, and not the backlog item.

In this case we will need to access the entity directly and not via the Backlog item. How does this sit with the proposed design? (I understand that this may be just an educational demo but how would someone have to think this if this was real life?)

Thanks in advance for any answers


Solution

  • In this case we will need to access the entity directly and not via the Backlog item. How does this sit with the proposed design?

    That depends: "access directly" is under specified.

    Bertrand Mayer's language of command query separation helps here. Queries leave the model unchanged; commands update the model.

    Here's the key idea: the unique concern of the aggregate root is commands; any change to the state of the aggregate is achieved by sending a command to the root entity, which may at its discretion delegate the responsibility of changing the state to some other entity.

    So if you are accessing the Task to query its current state, that's just fine. But getting the Task so that you can send commands to it directly? that breaks the rules. The aggregate root has the privilege of exclusive access to the commands of all of the entities within the aggregate.

    The implication is that you would never invoke Task.estimateRemainingHours() directly; you would instead invoke some analogous method on the aggregate root. BacklogItem.logEstimateFromTeamMember(), perhaps, which would in turn decide which tasks need to be updated.