Is a deep-rooted aggregate root suitable in DDD?

I have a system in which a user answers a question in a form. I have objects representing this model, but I'm not quite sure how to organize these objects in terms of DDD.

  • The form (has its own list) of sections;
  • Section β†’ (has its own list) of groups;
  • Group β†’ (has its own list) of questions;
  • Question β†’ (may have its own list of questions) Questions;
  • Question β†’ (there is own list of answers)
  • Answer β†’ (has its own list) answer_Details;
  • Answer_Detail β†’ (maybe has its own list of additional information) Sub_Answer_Details.

Each object has more than 15 properties, and each of them does not make sense without its parent. According to DDD, I believe that a Form object should be an Aggregate Root, and all other objects should be value objects. This means that I only need a repository for the Form object. In this case, the FormRepository will be cluttered with all the CRUD methods for the child objects. Is my reasoning correct in terms of DDD? Is it normal that I get a very extensive unit? I believe that such a view can easily lead to performance problems.

+6
source share
2 answers

Yes, the deep hierarchy in DDD is beautiful.

Is it good that I get a very extensive unit? - if the reality is so complex and your domain model is better than you can understand, you will have a complex aggregate root.

Yep, Form must be an aggregated root.

all other objects should be value objects - wrong, all other objects should be non-aggregate root objects (with identifier) ​​without a repository to retrieve them. The Value object has no identifier, and the object of equality of values ​​is determined only by its attribute values, and not by equality of identifiers (more details here ).

In this case, the FormRepository will be cluttered with all CRUD methods for child objects - no, the repository should contain only methods related to the aggregated root, i.e. Get<T> , Save<T> where T : IAggregateRoot , once you get an instance of the aggregate root, you can go through the attributes and methods to get what you need. Example:

 var formId = 23; var form = _formRepository.Get(formId); var firstGroup = form.Sections.First().Group().First(); 

or better

 var groupIndex = 1; var firstGroup = form.GetGroupAt(groupIndex); 

Where

 public Group GetGroupAt(int groupIndex) { Sections.First().Group().ElementAt(groupIndex); } 

I believe that this view can easily lead to performance problems - if you use CQRS , you will call the Form domain method from the command handler, and if you use NHibernate to save the entity, it will use lazy loading by default and will only load Form from the database, and then load only those objects that you really touch, therefore, for example, Sections.First() will load all sections from the database, but not the groups, but the rest. For queries, you must create a FormDto object (data transfer object) and other, possibly dtos smoothed, to get the data in the required form (which may differ from the structure of your objects, and the user interface can control the structure of dto). Check out the blog for info on DDD / CQRS / NHibernate / Repository

+3
source

Even if the answer was accepted, I thought I could add 2 cents:

Deep hierarchies are (possibly) fine, but remember that the idea of ​​an aggregate is to actually prevent this. I tend to think of entities collectively line by line:

"Does this object make any sense without AR?"

Since I do not have any wrt context of your model, I will use Order / OrderLine . Does OrderLine matter without Order ? Can I do anything (behavior) with the order line? The obvious answer here is no.

Each model must be contextualized. But ownership does not necessarily mean containment.

It may be easier to see when you are working with individual limited contexts if you return to BCs :)

In your case, Answer may not matter without Question . But perhaps Question can live in QuestionBank BC, and a specific question can be used in both your Examination BC and your Enrollment BC. This is all fully compiled, so it will depend on your context.

So, if this is the case where Question may be AR, then the questions belonging to your ARM Form may simply be a Value object or even a simple QuestionId.

+4
source

All Articles