Loading up all entities as part of an Aggregate Root

Viewed 542

From what I've read online an Aggregate when loaded from the database must be in the complete state. Meaning it must have access to all the entities inside of it also loaded from the database so that the aggregate is never in an invalid/incomplete state.

What if the aggregate contains an entity of which there can be millions of for eg. Aggregate A contains Entity B and Entity C. Now worst case scenario there can be upto 100 Entity B instances under the Aggregate A but there can be millions (if not billions) of Entity C under Aggregate A.


The Use Case:

The use case would be that I want to remove one particular instance of Entity C from Aggregate A using an Id. To do this in the DDD fashion I would have to first load up the Aggregate A from the database and load all it's Entities into memory. and then remove the item maybe by using a method like below:

public class AggregateA extends AbstractAggregateRoot<AggregateA>{

     private String aggregateId;

     private Map<String, EntityC> entityC;

     public void removeEntityC(String idToRemove) {
           this.entityC.remove(idToRemove);
           registerEvent(new EntityCRemoved(aggregateId, idToRemove));
     }
}

The question:

Loading up millions of entities into memory to perform any Write Operation (Command in CQRS) for a single request doesn't seem the right way.

Am I missing something here?

2 Answers

You misunderstand the "load the complete aggregate" mandate of DDD. Like you said, it's wholly impractical to do so. My recommendation is to load the complete parts of the aggregate that is needed.

For example: My action (albeit contrived) is to edit a specific blog comment in a blog entry with millions of comments. What I'll do is load the blog aggregate and the single comment I am trying to update from the repository. I'll then go ahead and have the blog aggregate update the comment. Ofcourse, another design choice would be to load just the comment (since you can identify it with it's id) but that would be loading an incomplete entity since it'd be missing the Aggregate root (that is what the mandate is telling not to do).

One issue here ofcourse is that it becomes in-efficient to do batch updates in DDD. That is a weakness by design and a problem solved outside of DDD.

I think this would be a first sign to rethink your aggregate boundaries. One possible solution would be to treat Entity C as an aggregate and have a reference by id to Aggregate A. It depends on your business domain, so this is only a vague recomendation. You could have a factory method in Aggregate A or in a seperate domain service which generates an instance of Aggregate C. In general you should avoid loading hundreds of Entities within an Aggregate, because it will span a huge transactional boundary arround that aggregate.

public class AggregateA {
    public AggregateC createAggregateC(...){
        //create AggregateC and return it with reference set to AggregateA
        return new AggregateC(this.id, ...)
    }
}
Related