0
votes

I am having a debate at work about aggregate roots and using the navigational properties to traverse through child objects.

Scenario 1:

  • Customer is the aggregate root
  • Address is an entity
  • Contact is an entity

A customer can have multiple addresses and an address can have multiple contacts. I query the customer repository below using the customer Id and return a customer object.

Dim customer as Customer = _customerRepository.GetById(request.Id)

If we need to acess the customers address we then traverse through the address within the customer object like below.

Dim address as Address = customer.RetrieveAddress(request.AddressId)

This approach would then be carried on for x number of child objects. The example I have shown is a simple one but in terms of DB tables containing millions of records how have other people managed with performance issues when traversing through several child objects once the aggregate root object has been queired and returned?

Scenario 2:

Same example above but instead of querying the customer repository and returning a customer object, we return a child object instead.

Dim address as Address = _customerRepository.GetAddressById(request.AddressId)

Now because we have queried the address object it means I don't have to traverse through the customer object to get to it. Even though I have used the customer repository to query the address table directly and return the address object is this allowed when following DDD? Or should I be using scenario 1 where I query the customer repository and return the customer object which is the aggregate root and traverse through the child objects?

The reason I ask is because in our DB diagram we have several tables to traverse through from our aggregate root and potentially it can contain millions of records over time which will reduce performance.

Just wondering how other people have manage to apply DDD thoroughly without reducing performance because when using EF with the navigational properties as soon as you use them it then sends a query for each child object which potentially sends 100+ queries if it is in a for loop.

Mike

2

2 Answers

1
votes

You will not be able to get very far using an ER diagram to determine aggregates :)

Ownership does not necessarily infer aggregation. You are simply referring to relationship traversal. Aggregate Roots are a tricky business. I have blogged about this a bit on my site http://www.ebenroux.co.za and you may want to look at this in particular:

Natural vs. Synthetic Aggregates

An aggregate has a distinct boundary that requires specific domain knowledge to determine where that boundary is. After that is simply becomes ownership or a weak reference.

1
votes

Aggregates are a tricky business and deciding on aggregate boundaries requires a lot of thinking. Reading your question I'm not quite sure you're designing your aggregates based on behaviour rather than DB entity relationships. As Eben says, you won't get very far with the latter approach, and your aggregates will probably end up being quite large (in terms of number of child entities).

One of the most insightful things I've read on the topic is Effective Aggregate Design by Vaughn Vernon. I'd definitely recommend giving it a read. One important thing he talks about is trying to make your aggregates as small as possible. This will naturally help with performance.

For child entities I would not do what you've done in scenario 2, because you should not be able to mutate the state of an entity without doing so via the aggregate root; this maintains the invariants. Having said that, your example uses an address object which is likely to be a value object, so having a separate address store for performance reasons would be fine.

When starting with DDD, I think its important to be reminded that you still need to be pragmatic with your design choices; DDD doesn't solve all your problems for you. In most cases the design choices are a trade-off for things like performance.