How to design great Aggregate Roots in Domain-Driven Design

  Рет қаралды 15,706

Milan Jovanović

Milan Jovanović

Күн бұрын

Пікірлер: 56
@MilanJovanovicTech
@MilanJovanovicTech 6 ай бұрын
Want to master Clean Architecture? Go here: bit.ly/3PupkOJ Want to unlock Modular Monoliths? Go here: bit.ly/3SXlzSt
@vantavoids
@vantavoids 6 ай бұрын
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
@MilanJovanovicTech
@MilanJovanovicTech 6 ай бұрын
Glad it was helpful, and no worries I don't have a mic anywhere (or do I?)
@enricoroselino7557
@enricoroselino7557 Ай бұрын
milan sure have mic everywhere 😂
@sergiom.954
@sergiom.954 6 ай бұрын
The value of your content is unique. Thanks Milan
@MilanJovanovicTech
@MilanJovanovicTech 6 ай бұрын
No, thank you! :)
@ferventurart
@ferventurart 5 ай бұрын
Great Milan, your explain a complex concept in a simple example! 👏
@MilanJovanovicTech
@MilanJovanovicTech 5 ай бұрын
Glad you liked it!
@BlindVirtuoso
@BlindVirtuoso 6 ай бұрын
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.
@MilanJovanovicTech
@MilanJovanovicTech 6 ай бұрын
Wanted to focus on maintaining a consistency boundary
@davidjiang7929
@davidjiang7929 5 ай бұрын
Would you happen to have more examples on the unit of work and how to save aggregate to database?
@MilanJovanovicTech
@MilanJovanovicTech 5 ай бұрын
Well, I've got a bunch of videos on the channel covering DDD aggregates directly or indirectly
@alexramossilva
@alexramossilva 6 ай бұрын
Can you talk about Business Rule Engines and what are the design patterns appropriate to implement it?
@MilanJovanovicTech
@MilanJovanovicTech 6 ай бұрын
Not familiar with them
@Luke_Ainsworth
@Luke_Ainsworth 3 ай бұрын
Hey Milan, Great video! I notice in some of your other examples around Aggregate Roots, you'll use an aggregate root class which is just an entity that handles your domain event logic as-well. Have you moved away from Aggregate Root classes/marker interfaces?
@MilanJovanovicTech
@MilanJovanovicTech 3 ай бұрын
Yes, correct. I just have an entity with domain events inside. The aggregate is enforced as logical concept in my design. Not much value in having an explicit class/interface.
@JonathanW-k6c
@JonathanW-k6c Ай бұрын
Sorry for asking another question about my previous problem (multiple prices in different currencies for products, shipping methods, and other aggregates). Could you give me a hint, if I had to choose between aggregates and child entities (for each aggregate with prices) to represent price, which one should I prefer?
@MilanJovanovicTech
@MilanJovanovicTech Ай бұрын
I don't know man 😅 This type of modeling question requires much more context than could fit in a YT comment.
@vuhoang5903
@vuhoang5903 Ай бұрын
Should I let an aggregate reference an entity inside another aggregate but not go through the root? For example Restaurant has Menu which has a list of Dish. Coupon Aggregate has a scope that is applicable to specific dishes so there should be a collection of applicable dish ids in the coupon aggregate right?
@MilanJovanovicTech
@MilanJovanovicTech Ай бұрын
Yes, that is acceptable. Holding references (IDs) to other entities is fine.
@pvlzh
@pvlzh Ай бұрын
Thanks for the video. In the example, you started taking the entity id instead of the entity itself to delete the record (link record). But what if I need to add a link to an existing entity? Is it right that in this case it will have to be obtained from outside and there is no other option?
@MilanJovanovicTech
@MilanJovanovicTech Ай бұрын
Yes, correct
@MdSayeedRahman-h3e
@MdSayeedRahman-h3e 3 ай бұрын
Great content!
@MilanJovanovicTech
@MilanJovanovicTech 3 ай бұрын
Thanks a lot :)
@JonathanW-k6c
@JonathanW-k6c Ай бұрын
Hey, nice video! What if I need store prices for the products, shipping methods, and some other aggregates in different currencies (country, currency, and amount)? Should I store it as a list of value objects for each entity, child entities or maybe separate aggregates?
@MilanJovanovicTech
@MilanJovanovicTech Ай бұрын
That would depend on how much information we're storing. If the lists are bounded (finite # of elements), that could be a viable option.
@victorgarcia3526
@victorgarcia3526 6 ай бұрын
Hey Milan, what if you push your code examples to github so we can check the code while watching the video?
@MilanJovanovicTech
@MilanJovanovicTech 6 ай бұрын
I share the code on Patreon
@maxencemartin6498
@maxencemartin6498 2 ай бұрын
Hi Milan, thank for your video. Juste a question remaining in my mind. Does this mean that we have to pass through an aggregate in order to mutate state of an internal entity instead of passing by the repository of this entity ? Does this mean that in a context of a command, that we do not need of repository for the entity access because aggregate act like that.
@MilanJovanovicTech
@MilanJovanovicTech 2 ай бұрын
Is there a business use case for that? Or is it just CRUD?
@maxencemartin6498
@maxencemartin6498 2 ай бұрын
@@MilanJovanovicTech This is a business use case.
@thomaswoods1365
@thomaswoods1365 4 ай бұрын
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.
@MilanJovanovicTech
@MilanJovanovicTech 4 ай бұрын
What is the "it" that's set to modified?
@thomaswoods1365
@thomaswoods1365 4 ай бұрын
@@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.
@thomaswoods1365
@thomaswoods1365 4 ай бұрын
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.
@thomaswoods1365
@thomaswoods1365 4 ай бұрын
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?
@thomaswoods1365
@thomaswoods1365 4 ай бұрын
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.
@wannadie9950
@wannadie9950 6 ай бұрын
awesome job milan, love you
@MilanJovanovicTech
@MilanJovanovicTech 6 ай бұрын
Thanks a lot! :)
@mahmoudali1660
@mahmoudali1660 4 ай бұрын
What do you mean by the use case? Thank you
@MilanJovanovicTech
@MilanJovanovicTech 4 ай бұрын
A component providing the desired functionality for one feature
@baranacikgoz
@baranacikgoz 6 ай бұрын
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.
@MilanJovanovicTech
@MilanJovanovicTech 6 ай бұрын
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.
@euler.chavez
@euler.chavez 6 ай бұрын
@@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?
@ChristofferLund
@ChristofferLund 6 ай бұрын
@@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
@kaozryan
@kaozryan 5 ай бұрын
@@ChristofferLund I'm curious how to solve this also. I think it will be a domain service.
@mahyarazad
@mahyarazad 4 ай бұрын
The hidden gem inside this video could be the IReadOnlyList 🤔😎
@MilanJovanovicTech
@MilanJovanovicTech 4 ай бұрын
Never ran into it?
@shakhriyorakhadov8244
@shakhriyorakhadov8244 5 ай бұрын
awesome
@MilanJovanovicTech
@MilanJovanovicTech 5 ай бұрын
Thanks!
@swift8995
@swift8995 Ай бұрын
Awesome content! I have a question, can I store an aggregate root id inside a value object? For example I have a currency and country as aggregates, can I include their ids in the address and money value objects?
@MilanJovanovicTech
@MilanJovanovicTech Ай бұрын
Currency isn't really an aggregate, though. I consider it more as a value object.
@swift8995
@swift8995 Ай бұрын
​@@MilanJovanovicTech But what if I need to store exchange rates periodically? Or better way to get conversion every time from third party API?
A Natural Path to Domain-Driven Design (evolve your code)
26:05
Milan Jovanović
Рет қаралды 15 М.
Увеличили моцареллу для @Lorenzo.bagnati
00:48
Кушать Хочу
Рет қаралды 8 МЛН
How To Choose Mac N Cheese Date Night.. 🧀
00:58
Jojo Sim
Рет қаралды 88 МЛН
ТЮРЕМЩИК В БОКСЕ! #shorts
00:58
HARD_MMA
Рет қаралды 2,6 МЛН
The Singing Challenge #joker #Harriet Quinn
00:35
佐助与鸣人
Рет қаралды 44 МЛН
How To Use Domain-Driven Design In Clean Architecture
30:27
Milan Jovanović
Рет қаралды 112 М.
Singleton pattern
8:02
AbanoubTalaat (NoubCode)
Рет қаралды 58
Modeling a Domain With Domain-Driven Design From Scratch | DDD
19:10
Milan Jovanović
Рет қаралды 92 М.
Domain-Driven Design: The Last Explanation You'll Ever Need
21:05
Software Developer Diaries
Рет қаралды 10 М.
Microservices with Databases can be challenging...
20:52
Software Developer Diaries
Рет қаралды 94 М.
Completely Get Rid of Exceptions Using This Technique
19:24
Milan Jovanović
Рет қаралды 26 М.
Master the Fluent Builder Design Pattern in C#
15:05
Milan Jovanović
Рет қаралды 28 М.
Увеличили моцареллу для @Lorenzo.bagnati
00:48
Кушать Хочу
Рет қаралды 8 МЛН