Sorry for poor English.
When learning about DDD , i have a question about aggregate.
In Board domain, we have two entity, One is Post, another is Comment.
I think Comment can't exist without Post. So I think Comment should be in Post Aggregate and Board domain has one aggregate which root is Post.
But from a different perspective, entities in the same aggregate have same life cycle,
so Comment life cycle is different from Post's life cycle,
and modifying Post doesn't affect Comment and vice versa.
So Board domain has two aggregate,
one is aggregate which root is Comment and another is aggregate which root is Post
This two think makes me feeling mess.
I've been there before, and I learned it the hard way. At first, it seems natural to have the comments part of the post aggregate but here are the limitations you might face with this design.
It can happen that you need to delete the post and keep the comments. Same as you see on some social media platforms.
A post can have a lot of comments to the point that it becomes expensive to load them all in memory each time you fetch a post.
Users can interact with comments directly (reactions, comment on a comment, share, mention other users ...) hence it is easier if the comment can be uniquely identified across the system.
You might need to list all comments of a user. It becomes harder when the comment is part of the post aggregate.
It is safer to have PostComment as a separate aggregate and keep a reference of the post. And as a general rule for designing aggregates, you should keep them as small as possible.
I recommend to read this brilliant article. That changes your perspective to aggregate design rules.
Effective Aggregate Design Part I: Modeling a Single Aggregate
Related
I am studying hard DDD now. After months of studying, I found a contradiction between modeling and coding. As you know, DDD has the principle of "Domain 1st, Tech 2nd". However, there are "definite" constraints when moving the actual model into the implementation, for example:
Original model:
With the domain concept, posts are associated with multiple comments, and comments are an ordinary model that depends on the post. Moving this into code ...
class Post{
Integer postId;
String title;
String content;
String writer;
Collection<Comment> comments;
}
class Comment{
String content;
}
Like this... But if there are hundreds of millions of comments, there will be performance problems. According to the solution I have investigated, finally I change the original model as follows....
Revised model:
Also the code will change...
class Post{
Integer postId;
String title;
String content;
String writer;
}
class Comment{
Integer postId;
String content;
}
This revised model works well for me and I am satisfied with the solution. But I feel something inconsistent. It seems that the model is distorting due to technical constraints. If we show the first and second models to the domain experts and talk about the diagram, he or she will better understand the first model.
Am I misunderstanding about DDD concept? If so, give me some advice. Thank you.
One of the changes in mindset that come with Domain Driven Design is that your domain will constantly evolve with changes to your understanding of the domain. It is not just that the domain will evolve, but your understanding and perception about it will evolve, as well.
What you are running into right now, in my opinion, is trying to rationalize the design based on the loading of one or more entities into an aggregate by looking at it from how it is persisted. This common parent and child relationship feels natural, because it is the way that most of us have done things in the past. Even from a DDD perspective, it is easy to get caught up in the "Comments can't exist without Posts" paradigm.
This comes down to the fact that you are modelling based upon data inside of the domain, instead of use cases. Look at it this way... Posts are not simply a collection of Comments. Instead, a Comment refers to a specific Post. The nuance sounds minor, but it has broad-reaching consequences. When modeled in this fashion, it matches your revised model - and it is completely and totally fine. The only thing you need to change is your mindset, being that Comment can be considered an Aggregate, just as a Post is. They both are going to have use cases where the other one must exist, but at the same time, you need to see that you are unlikely to do something where both are impacted as part of the same use case (outside of deletion of the Post).
To illustrate, consider this use case:
As a Writer, I want to be able to edit the Content of my Post.
Your new model actually effectively supports that. Instead of looking at the relationship, you are looking at how the domain is used. Should you load Comments for a Writer to edit? Without knowing your domain, I would still assume that you would not want to do so. There are use cases that will likely involve both Posts and Comments, and that is also fine. Since you have two Aggregates inside of a single Bounded Context, you will be able to support use cases that are solely based on Posts, solely based on Comments, or a combination of both.
In terms of the technical concerns, you mention "hundreds of millions of comments". I assume that you mean system-wide, and not for a single Post? Assume that you have an active post, and that it sees 20k comments in its lifetime. For a properly designed and optimized database, this should still not be an issue. When it becomes an issue, if it ever does, it can be further addressed by additional changes to the technology used, as opposed to changing the domain. You can look at things like caching strategies (outside of the domain, since that is an application concern and not a domain concern), external indexes, eventual consistency, etc.
Highly recommend taking a read through Vaughn Vernon's "Effective Aggregate Design" series of articles, if you have not already:
Effective Aggregate Design
Additionally, his excellent book "Implementing Domain-Driven Design", is a must-read, in my opinion. While the Evans material is essential, it is more like the theoretical concepts; whereas the Vernon material talks about how to put the concepts into practice and what the implications of those decisions are.
As an addendum, like plalx points out in the comments below. None of this is meant to be a generalization about Posts and Comments in general, but explicitly how they apply to your domain, as it has been described to us and what could be inferred from your models. Posts and Comments will likely behave differently for others, because others will have distinct use cases. Even within your own domain, should Posts and/or Comments exist in a different context, they could behave differently. Above all else, try and make sure that everything you add to your domain model (in general) is added because of a specific and direct use case. It is tempting to simply start data modeling, but you will start to find yourself trying to force use cases into the domain model. Domain modeling should not be a finite process, or a single step in the process. Be willing and able to adapt to changes in understanding or changes to the way that the business changes its overall operational strategies over time.
One pitfall I notice about people entering the DDD world is that they visualize their ARs as source of READ operations as well. So, many times they tend to "model" the ARs and its entities/value objects in a way that "appeals" a DTO that is meant to hydrate the UI.
In your case, I would make Post an AR and Comments another AR. Adding a comment shouldn't require to instantiate Post at all. Editing a Post shouldn't require to 'load' the comments at all.
Use an independent mechanism to project your post/comments into a POCO/DTO class that may make sense to have a collection of comments in your post.
Does it make sense?
I'm implementing a college system, and I'm trying to use DDD. I'm also reading the blue book. The basics entities of the system are Institution, Courses, Professors and Students. This system will allow a lot of Institutions, each having its courses, students and professors.
Reading about aggregates, all entities fits inside the aggregate Institution, because doesn't exists courses without Institution, the same for students and professors. Am I right thinking in that way?
In some place the professors will access the courses that they teach. Using this approach, should I always access the courses through Institution? This implementation seems strange to me, so I ask myself if Professor, as Students should be their own AR and have their Repository.
Even though you have accepted an answer I am adding this anyway since a comment is too short.
This whole aggregate root business trips up just about everyone when starting out with DDD. I know, since I have been there myself :)
As mentioned, a domain expert may be helpful in some cases but keep in mind that ownership does not imply containment. An Order typically belongs to a Customer but the Customer is not the AR for an Order since an Order can exist without a Customer. You may think: "But wait, that isn't really true!". This is where is comes down to rules. When I walk into a clothing store I can purchase a pair of shoes. I am a customer but they have no record of me other than a receipt I can produce. I am a cash customer. Perhaps my particular brand of shoe is not in stock but I can still order it. They will contact me once it arrives and that will probably be that and I'll in all likelihood still not be registered in any computer system. However, that same store is registered as a Customer with their supplier.
So why this long-winded story? Well, if it is possible to have an Entity stand alone with only a Value Object representing the owner then it is probably going to be an AR. I can include some basic customer information in a CustomerDetails value object in an Order? So the Order can be an AR.
No let's take a look at an OrderLine. Can I include some basic OrderDetails information on an OrderLine? This feels odd since a number of order lines constitute an Order. So it isn't quite as natural.
In the same way a GrapeBunch has to have a GrapeStem and a collection of GrapeBerry objects.
This seems to imply that if anything can be regarded as optional it may indicate that the related instance is an AR. If, however, a related instance is required then it is part of the AR.
These ideas are very broad but may serve as guidelines to consider your structure.
One more thing to remember is that an AR should not be instanced in another AR. Rather use the Id or a Value Object representing the relationship.
I think you're missing some transactional analysis - what typically changes together as part of the same business transaction, and how frequently ? One big aggregate is not necessarily a problem if only 2 users collaborate on it with only a few changes per day, but with dozens of concurrent modifications it could become a contention point.
Besides the data inventory and data structuration aspect of the problem, you want to have an idea of how the system will be used to make educated aggregate design decisions.
Something that might help you to separate those entities into different aggregate roots is to ask you: Which one of those must be used together? This is usually helpful as a first coarse filter.
So, for example, to add a student to a course, you don't need the Institution?
In your example about a professor accessing the courses he teaches. Can he access them by providing his professor id rather than the professor entity? I he provides the professor id, then the entities won't be associated by a reference but by an id.
Lots of this concepts have evolved a lot since the blue book was written 12 years ago. Even though the blue book is a really good book, I suggest you to also read the red book (Implementing Domain-Driven Design by Vaughn Vernon). This book has a more practical approach to DDD and shows more modern approaches, such as CQRS and Event Sourcing.
A professor and a student can exist in their own right, indeed they may associate themselves with institutions. An institution exists in its own right. A course may exist in its own right (what if the same course is offered at more that one institution, are they the same?)... The domain expert would best advise on that (infact they should advise and guide the entire design).
If you make an aggregate too big you will run in to concurrency issues that can avoided if you find the right model.
Some PDFs I recommend reading are here:
http://dddcommunity.org/library/vernon_2011/
I have two an aggregate root referencing another aggregate root (first references the second via the identity of the second aggregate root).
A command from my application layer (via MVC asp.net) now deletes my second aggregate root.
At the point of deleting the root, do I send a Domain Event telling the first aggregate root to "NULL" the reference to the second aggregate which now does not exist?
JD
You're going about it the wrong way. Step back from the technical issue you are facing. First of all I doubt there is such a thing as "Delete" in your ubiquitous language. Most likely people will call it "archive", "taking out of order", "remove", "out of stock", ... some term that denotes that a particular aggregate is at the end of its life-cycle. When domain experts speak of such things, this should be a trigger for you to ask them a question along the line of : "Well if you discontinue a Product, how will that affect a Promotion for that particular Product?". To correlate back to your issue: Promotion being the aggregate that holds a reference to the Product aggregate. So it very much becomes a business issue rather than a technical one. Most of the time business people already have a process in place that prevents this technical issue from happening in the first place (e.g. you can't discontinue a Product that's being used in a Promotion). I hope it's clear by now that giving you a generic answer is not an option.
I think the question was not actually answered by another responder that has been here for a while. I also think that those who come here deserve an answer. I will answer the question (see #2), but let me first clarify something.
#1 Why Not Delete?
So, first, I agree with another answer and will say that in domain terms, there is no such thing as deletion typically. I like the angle from which this article by Udi Dahan explains the point.
My explanation for being against the deletion is a bit old-style, simpler (yes, even simpler), and hopefully easier to understand. Imagine a world without (or before) computers. People did everything on paper. Imagine (I know it might be awkward) that you are dealing with a book instead of the video. A book that contains the same info as the video you are deleting. You can't delete the book, but you can "burn" it or "trash" it. It actually continues living but changes its form of existence. Similarly, your video moves from its current state to the next state, or maybe it even transforms and gives birth to the object that is its next state. Anyway, the point is, you are changing the state and not deleting it. So, it would be best if you modeled it as a state change. That was the point, although I know it could have been unclear.
#2 How to Update Other Aggregates?
Very simple, the original aggregate (the one that changes state, see above) dispatches an event that describes what happened to it. You subscribe to that event, and the event handler updates other impacted aggregates. I think this was the answer you were looking for without all the "can't delete" conversations. Sorry that I added that part again, but I wanted to clarify both points for readers.
To expand the book's example: when you trash the book, you need to update the book directory (or index paper that shows where each book is in the library), not to mention the trashed book anymore. So without computers, the one who trashed the old book would tell the other guy responsible for the directory to scratch the book from the list (read as "when the book is trashed, the directory must remove it from the list"). That is what the event accomplishes but in digital terms.
Domain Driven Design can be really confusing at times and since I am rather new to this technique I would like to have some answers regarding those scenarios that are currently bugging me.
Here a simple diagram to represent my question using DDD principles. My question is about aggregate roots, domain validation and "ways to do" or best practices.
In this scenario, how would you implement a way to count the number of comments written by a user? Would it be a method in "Review"? Or would it be best to be a method in a repository (ReviewRepository)?
How do I make other entities access comments if they need to? Having this scenario, does that mean that Comment isn't part anymore of the "Review" aggregates?
What if comment have a composition relationship with some other entity? How would you manage the access to that entity? Is comment responsible of this entity or the root?
Any other suggestions or facts regarding this model? Any best practices I should fellow when designing a model?
Thanks.
NOTE: The answer must fellow DDD principles
There a little error in the Review entity. "Compte" in the Add method is "Account" and should be A instead of C.
In this scenario, how would you implement a way to count the number of comments written by an user ?
Responsibility belongs with review. It's an aggregate of comments. Count is a first-class feature of any aggregate.
How do i make others entities access comments if they need to ?
Comments are accessible via a Review. A Review is an aggregate of comments.
What if comment have a composition relationship with some other entity ?
"What if" questions are hard to answer without a concrete and specific example. After all, the design is driven by the problem domain, not random thoughts.
If some "other" entity also appears to be a composition of Comments, you have to go back to the domain experts and try to determine where the real responsibility lies.
One pair of question is "if the review is removed, what happens to the comments?" and "If the mysterious 'other' is removed, what happens to the comments?" This can help find the responsibilities.
As an exercise I am trying to create a simple blog app in .NET, employing DDD. So far I have User, Topic and Comment classes. But the problem is how to link Comment with User and Topic? If I say that User and Topic are aggregate roots, where does Comment belong?
Aggregates may have associations to other aggregate roots.
e.g. a comment may have an association to both topic and user.
Thats how I would model it anyway, aggregate roots are boundaries of consistency.
A Topic does not need to be consistent in any way with its comments, thus comments does not need to be part of that aggregate.
A user does not need to be consistent with all of his or her comments so users does not need to know about comments either.
I would fetch the comments for a specific topic by the comment repository.
e.g.
var comments = commentRepo.FindCommentsForTopic(someTopic);
//or by topic id for pragmatic reasons.