Domain-Driven Design – Updating Deep Child Objects on an Aggregate Root

domain-driven-design

How should I perform updates on nested children of an Aggregate Root?

Should I find the child object by traversal of associations and perform the update on it directly, or should I add a method on the Aggregate Root that takes care of it?

An example

I'm modelling an old project at our company which is a system we use to perform searches of customers against a suspicious person list:

The model

  • We run a Batch Search every night
  • The Batch Search contains a list of Searches for it.
  • Each Search contains a list of Matches and is associated with one Customer

The process

  • We check each Match and mark it as confirmed if it was a confirmed match.
  • We also tick a checkbox for each Search if all it's Matches where inspected

DDD model in diagrammatic form of the above description

The caveat

A Search can also run standalone, as in – not being a part of a Batch Search. In that case the Search naturally becomes the Aggregate Root.

Question

How should I be performing the update operations on the child items? The blue book states that all updates to children must go through the Aggregate Root.

There are 2 ways to perform this update:

Option 1

Traverse the graph and find the child object and perform the operation directly on it.

batchSearch = batchSearchRepo.find(1);
search = batchSearch.findSearch(5);
match = search.getMatch(3);
match.markConfirmed();
batchSearchRepo.save(batchSearch);

Option 2

Add a method on the Aggregate Root

batchSearch = batchSearchRepo.find(1);
search = batchSearch.markSearchMatchAsConfirmed(5, 3);
batchSearchRepo.save(batchSearch);

Which of the 2 update methods is more appropriate and why?

Best Answer

Aggregate roots exist to protect the data within them. If you tear the aggregate roots internal structures and do operations on the internals directly without incorporating the aggregate root, the aggregate root can no longer validate the operation is valid.

In DDD the second option is not only more appropriate, it is the only correct way to go (as stated in the quote you posted).


When you however encounter the nesting that you have, you could also ask yourself a question whether your design is 100% correct.

  1. Aren't perhaps some of the internals of BatchSearch aggregate roots on their own?
  2. Couldn't maybe Search exist outside of the boundary of BatchSearch?
  3. Does affecting a Search affect a BatchSearch?

If you answered yes to questions 1 and 2 and no to the third question, you have already eliminated one entire level on nesting and can now access a Search directly as a separate aggregate root. With this in mind, could you maybe to the same for a Match?

Related Topic