Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to properly define an aggregate in DDD?

What would be a rule of thumb when designing an aggregate in DDD?

According to Martin Fowler, aggregate is a cluster of domain objects that can be treated as a single unit. An aggregate will have one of its component objects be the aggregate root.

https://martinfowler.com/bliki/DDD_Aggregate.html

After designing aproximatelly 20 DDD projects I am still confused about the rule of thumb when choosing domain objects that would create an aggregate.

Martin Fowler uses order and line-items analogy and I don't think it is a good example, because order+line-items are really tightly bounded objects. Not much to think about in that example.

Lets try with car analogy where CarContent is a subdomain of a car dealer domain.

CarContent would consist of at least one or more aggregate/s.

For example we have this AggregateRoot (i am keeping it as simple as possible)

class CarStructureAggregate
{
     public int Id {get; private set;}
     public ModelType ModelType {get; private set;}
     public int Year {get; private set;}
     public List<EquipmentType> {get; private set;}
}

Alternative could be this (example B)

class CarStructureAggregate
{
     public int Id {get; private set;}
     public ModelType ModelType {get; private set;}
     public int Year {get; private set;}
}

class CarEquipmentAggregate
{
    public int Id {get; private set;}
    public List<EquipmentType> {get; private set;}
}

Car can be created without equipment but it cannot be activated/published without the equipment (ie. this can be populated over two different transactions)

Equipment can be referenced trough CarStructureAggregate in example A or using CarEquipmentAggregate in example B.

EquipmentType could be an enum, or could be a complex class with many more classes, properties.

What is a rule of thumb when choosing between examples A and B? Now imagine that car could have more information such as

  • photos
  • description
  • maybe more data about the engine

and CarStructureAggregate could be an extremely large class

So what is it that makes us split Aggregate into new Aggregates? Size? Atomicity of a transaction (although that would not be an issue since usually aggregates of a same sub domain are usually located on the same server)

like image 796
mko Avatar asked Jul 09 '18 10:07

mko


People also ask

How is aggregate DDD defined?

Aggregate is a pattern in Domain-Driven Design. A DDD aggregate is a cluster of domain objects that can be treated as a single unit. An example may be an order and its line-items, these will be separate objects, but it's useful to treat the order (together with its line items) as a single aggregate.

What are aggregates in a domain model?

An aggregate is a group of objects that must be consistent together, but you cannot just pick a group of objects and label them an aggregate. You must start with a domain concept and think about the entities that are used in the most common transactions related to that concept.

What are aggregates in bounded context?

Aggregates represent business entities, and thus are smaller in scope than Bounded Contexts, which represent entire domains. Aggregates encapsulate data, whereas Bounded Contexts encapsulate services, team members, and their shared language(s)

How do you find the aggregate root?

One good way of identifying the aggregate root is to use the "delete" test. In your domain if you delete the root, what is deleted with it? This way you can identify domain object ownership, which is a trait of Aggregates.


3 Answers

Be careful about having too strong OO mindset. The blue book and Martin Fowler post are a little bit old and the vision it provides is too narrow.

An aggregate does not need to be a class. It does not need to be persisted. Theese are implementation details. Even, sometimes, the aggregate do things that does not implies a change, just implies a "OK this action may be done".

iTollu post give you a good start: What matters is transactional boundary. The job of an aggregate is just one. Ensure invariants and domain rules in an action that, in most of the cases (remember that not always), change data that must be persisted. The transactional boundary means that once the aggregate says that something may, and has, be done; nothing in the world should contradict it because, if contradiction occurs, your aggregate is badly designed and the rule that contradict the aggregate should be part of aggregate.

So, to design aggregates, I usualy start very simple and keep evolving. Think in a static function that recives all the VO's, entities and command data (almost DTO all of them except the unique ID of the entities) needed to check domain rules for the action and returns a domain event saying that something has be done. The data of the event must contain all data that your system needs to persist the changes, if needed, and to act in consequence when the event reach to other aggregates (in the same or different bounded context).

Now start to refactoring and OO designing. Supress primitive obsession antipattern. Add constraints to avoid incorrect states of entities and VO's. That piece of code to check or calculate someting related to a entity better goes into the entity. Put your events in a diet. Put static functions that need almost the same VO's and entities to check domain rules together creating a class as aggregate root. Use repositories to create the aggregates in an always valid state. And a long etc. You know; just good OOP design, going towards no DTO's, "tell, don't ask" premise, responsibility segregation and so on.

When you finish all that work you will find your aggregates, VO's and entities perfectly designed from a domain (bounded context related) and technical view.

like image 79
jlvaquero Avatar answered Oct 05 '22 23:10

jlvaquero


Something to keep in mind when designing aggregates is that the same entity can be an aggregate in one use case and a normal entity in another. So you can have a CarStructureAggregate that owns a list of EquipmentTypes, but you can also have an EquipmentTypeAggregate that owns other things and has its own business rules.

Remember, though, that aggregates can update their own properties but not update the properties of owned objects. For example if your CarStructureAggregate owns the list of EquipmentType, you cannot change properties of one of the equipment types in the context of updating the CarStructureAggregate. You must query the EquipmentType in its aggregate role to make changes to it. CarStructureAggregate can only add EquipmentTypes to its internal list or remove them.

Another rule of thumb is only populate aggregates one level deep unless there is an overriding reason to go deeper. In your example you would instantiate the CarStructureAggregate and fill the list of EquipmentTypes, but you would not populate any lists that each EquipmentType might own.

like image 42
Brad Irby Avatar answered Oct 06 '22 00:10

Brad Irby


I believe, what matters here is transactional boundary.

On one hand, you can't establish it more narrow than it is sufficient for preserving an aggregate's consistency.

On the other hand, you don't want to make it so large to lock your users from concurrent modifications.

In your example, if users should be able to modify CarStructure and CarEquipment concurrently - then I'd stick to implementation B. If not - it would be simpler to use A.

like image 30
iTollu Avatar answered Oct 06 '22 00:10

iTollu