How to properly define an aggregate in DDD?

旧街凉风 提交于 2019-12-23 13:39:20

问题


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)


回答1:


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.




回答2:


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.




回答3:


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.



来源:https://stackoverflow.com/questions/51243959/how-to-properly-define-an-aggregate-in-ddd

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!