How to design great Aggregate Roots in Domain-Driven Design
ฝัง
- เผยแพร่เมื่อ 6 ส.ค. 2024
- ☄️ Master the Modular Monolith Architecture: bit.ly/3SXlzSt
📌 Accelerate your Clean Architecture skills: bit.ly/3PupkOJ
🚀 Support me on Patreon to access the source code: / milanjovanovic
In this video, I'll do a deep dive into the theory behind Aggregates. I'm going to explain how to design an aggregate root and implement it in your .NET applications. The aggregate root pattern from Domain-Driven Design can help you create a better domain model. An aggregate acts as a consistency boundary and encapsulates one or more entities. Designing a good aggregate takes a lot of skill and practice.
Join my weekly .NET newsletter:
www.milanjovanovic.tech
Read my Blog here:
www.milanjovanovic.tech/blog
Chapters
0:00 What is an Aggregate?
1:05 Why do we need Aggregates?
2:15 What is an Aggregate (continued)
5:42 Aggregate design best practices
7:30 Let's look at an Aggregate example - วิทยาศาสตร์และเทคโนโลยี
Want to master Clean Architecture? Go here: bit.ly/3PupkOJ
Want to unlock Modular Monoliths? Go here: bit.ly/3SXlzSt
honestly the fact that i was struggling all day with my aggregate roots and you happen to release a video talking about those specially... did you hide a mic under my desk??
other than that top tier video as per usual please keep it up
Glad it was helpful, and no worries I don't have a mic anywhere (or do I?)
The value of your content is unique. Thanks Milan
No, thank you! :)
Great Milan, your explain a complex concept in a simple example! 👏
Glad you liked it!
awesome job milan, love you
Thanks a lot! :)
awesome
Thanks!
Hi Milan. Nice video, appreciate it. Though not so good example on Workout aggregate, not showing any invariant between Workout and Exercises. Aggregate is about behavior and invariants and not about data.
Wanted to focus on maintaining a consistency boundary
Can you talk about Business Rule Engines and what are the design patterns appropriate to implement it?
Not familiar with them
Would you happen to have more examples on the unit of work and how to save aggregate to database?
Well, I've got a bunch of videos on the channel covering DDD aggregates directly or indirectly
What do you mean by the use case?
Thank you
A component providing the desired functionality for one feature
Hey Milan, what if you push your code examples to github so we can check the code while watching the video?
I share the code on Patreon
Do you have this full course?
Not really (yet)
Milan I implemented this full model and got update exceptions. I want to be able to add and delete within the aggregated tables but every add I do is recorded as a changestate modified not an insert. Is there some way to have it work correctly? I confirmed that if I use the dbContext and add directly to the entity object it is in a change state of Added. If I do not use the dbContext and add inside the aggregate root it is set to Modified.
What is the "it" that's set to modified?
@@MilanJovanovicTech the "it" is a child table. Example: Orders and OrderDetails. If I create an add method for OrderDetails in Orders aggregate root, as you have defined in your example, the add produces a change state Modified not Added. Throws a DbUpdateConcurrency exception because the row is not in the database yet. If Aggregate Root pattern does not intend to be used like this please let me know. In your example I'm doing exactly as you are adding an exercise to the workout aggregate root. If yours works and mine throws an exception I'm not sure why.
I just did some deeper study on AggRoots. I get the feeling that AggRoot concept by definition requires that all records within the aggregate be created at the same time because of the interdependency that justifies the AR implementation. In the case of Order/OrderDetail it may be that it is NOT an AR model. Would like to get your opinion on that.
Milan I have double checked the code. When you pushed down the Remove for exercise it deletes from the exercise collection within Workout domain object then you call SaveChanges(). The problem is the exercise collection is not tracked by EF. There is no insert getting written to the database for the Exercise added. This model of having an internal collection for the non-root aggregates is broken as I see it. There is no reference to the repository either, thus no way to cause the insert to occur. What is the right way to get an aggregate member inside the aggregate root to perform CRUD correctly?
Ok, I have the answer to this issue in case it is helpful to anyone else. If the entity you add to the collection has an ID assigned it will treat it as an UPDATE and change state will be set by EF to Modified. I removed the ID and it accurately set the change state to Added. The problem here Milan is that the entity base classes are not allowing nullable ID's, which means you will ALWAYS get a Modified state and never a Added state. I don't know what this means to the base classes you have defined Milan. I would love to understand if there is a better way to successfully handle this. I had to set every property inside all entities to nullable in order to get the right behaviors. I do not think that is necessarily a good thing.
The hidden gem inside this video could be the IReadOnlyList 🤔😎
Never ran into it?
The change you've done to the RemoveExercise method could not be left like that. You did not specified which Include's is used inside GetById method for Workout. If you decide to do that, it is wiser to conditionally include exercises of workouts by specifying exerciseId. Otherwise if there are millions of exercises you are loading all into memory just to remove one.
If there are a large number of exercises, we would implement this differently... But we could also impose a business constraint that a workout can have at most 30 exercises.
@@MilanJovanovicTech What is this process like, or what would it be like? The "basic" examples are easy to understand, but the complex cases and their variations are rarely explained.
For example, in a context where the Family is the aggregate, with entities such as Members and Addresses:
a) If I need to obtain only the members, do I always have to access the aggregate?
b) If I want to filter address based on type (house, work, business, etc), do I have to do it through the aggregate?
@@MilanJovanovicTech If you could provide an example of how you could elegantly solve this if the related entity has too many to load all into memory that would be nice. I find that to be one of the bigger challenges when using aggregates. Even just having a list of IDs could be too much at one point i presume
@@ChristofferLund I'm curious how to solve this also. I think it will be a domain service.