Nice one man. It's really cool that someone explains such stuff to the people who are not familiar with this yet. I with there were such videos when I was investigating this topic
Great video! I have a couple of questions: 1. What if Gathering is having 1000+ Invitations? Is it wise to fetch the entire Aggregate? 2. When dispatching domain events before committing changes to the DB. How do we handle Email/NotificationService correctly if case committing afterwards fails?
@@MilanJovanovicTech 1. You mean that in retrieving the aggregate, I add a condition on what related data to include? .Include(gathering => gathering.Invitations.Where(x => x.DateSent < date)) ? That would work! 2. My domain events are dispatched before persisting the changes to the DB. If you trigger sending an email. How would you handle that if the save fails? Sent a new e-mail? Perhaps acceptable in this case if requierments call for it. I’m looking forward to your upcoming video!
@@MilanJovanovicTech I gave it a through. When pre-dispatching events (before saving changes), instead of calling the e-mailservice directly you put the action in an outbox that can be committed after successfully saving the changes, as with integration events. My worries is about how to make that clean.
On first looking at the project, I would think that a Member sends an invitation. Understanding that the Gathering is sending the invitation (and managing the attendees) makes perfect sense but isn’t obvious to a newbie like me so thank you.
The question to ask in that case would be: "Who owns the invitation"? Is the the Member? Or the Gathering? I decided on Gathering. You may come to a different conclusion. And we could both be right 😁
Let's say, Post is the AggregateRoot and Comments are the children of Post. A post can have thousands of comments. How would you update a comment through the AggregateRoot? Obviously not pulling all the comments into memory just for updating a single comment. I think we should use filtered include for the children.
Filtered incldue definitely solve this problem, that's a great suggestion. But what I didn't cover in this video is that you have consider performance when designing Aggregates. In your example, performance would be severely degraded if we used Post as the AggregateRoot. The proper approach would be to have the Comment reference the Post by FK. That way we can only fetch that Comment, and modify it. We wouldn't have a "real" Aggregate,
The way I understand it is the aggregate root is meant to enforce invariants within the aggregate cluster and ensure they're accessed and modified in a consistent and controlled manner. The root protects the boundary around the aggregate cluster, which doesn't necessarily mean loading a ton of data just to modify bits of data.
So choosing an aggregate is mainly based on responsibility of the class. What you can identify as a single process and you treat those objects as one unit?
There's a bit more nuance to it. But that's the general idea. It's hard to define a set of rules. It kind of evolves on it's own as you build out the project.
I got the idea to maintain consistency in DDD, but performance is still an issue if I pull all that information from database. Some others folks also pointed that out here.
Absolutely, DDD should never be followed to the letter. You must be *pragmatic*. If you have a collection with a tendency to grow extremely large: post-comments (Reddit). Then we can throw out this design out the window. I'm really glad so many people are commenting about performance - it means I have good engineers watching these videos 😁
@@MilanJovanovicTech I think another way to reduce the complexity is what Eric Evans says about associations in the blue book. He says that there are at least three ways of making associations more tractable: 1) Imposing a traversal direction 2) Adding a qualifier, effectively reducing multiplicity 3) Eliminating nonessential associations In that case I believe number 2 may help to reduce the number of "valid" invitations. But I would appreciate a Milan´s video about it haha
I wonder, lets say i am trying to model the problem of doing some business operations on historical stock data, so a stock is basically a symbol (for example AMZN), and a time series that represents the data of that stock. Often we want to query and do operations on the stock data based on symbols, so ultimately my question is, should symbol also be an aggregate root, alongside with stock? If not, should i still have two separate repositories? If so, should I create a repository that encapsulates both of them? How would you go about handling this situation
I'd choose the approach that's easier to work with in terms of: - Storing data - Querying data Symbols are just like an enum value, right? They don't change much. You're more interested in the time series data of the stocks price changing over time.
@@MilanJovanovicTech I see, in my case, I want to allow the functionality of assigning tag to symbols, for example: S&P 500, bad stock, etc. So I think it is more fitting that the symbols would also be aggregates, as they are more complicated than a simple string, and also, while it is true that they don't really change overtime, I don't want to assume they are always existing in my DB, as I'd assume all enum values are in an enum
Don't we need to specify parameterless private constructor to let Entity Framework create an object of specific Entity ? But we should pass an Id to the constructor of the base class using your approach. How do you handle that ? Do you use specific Entity within DbContext?
If your only reading data, why is consistency and issue? Surely an aggregate is for only write actions, read actions should be allowed outside the aggregate if that functionality is necessary (I.e. you only care about a small subset of the aggregate data in some scenario)? If I want to list to the user all invitations for whatever reason, surely I shouldn’t have to pull that through the aggregate?
Hi Milan in the case that we have to return a dto to the client with data of multiples aggregates, how we can do that without doing multiple queries to the db? because we cant retrieve that data in one single query because the aggregates are not related in the properties so we cant include that data
Excellent question! Yes, definitely this does not hold in such a case. You couldn't have a collection so large in that case. You would have to make Invitations reference Gathering, to make the solution peformant. It's a lengthy discussion. I will try to find you some good reading resources, if there are any. Or just create a separate video on the topic.
I am trying to learn the aggregate root patterns but it seems hard for now. Can you please list down the tutorial links from beginning to intermediate so that we can follow them? For example in this video I can see u are adding Aggregate but what are value objects, Entity, how did u add them and which tutorial will show these things?
Hi Milan, since Gathering is the aggregate root, I'm confused that Member knows about the Id of the Invitation. Components outside of the aggregate should not be allowed to reference Ids of non-root entities.
@@MilanJovanovicTech According to Evans: Now, to translate that conceptual AGGREGATE into the implementation, we need a set of rules to apply to all transactions: • The root ENTITY has global identity, and is ultimately responsible for checking invariants. • Root ENTITIES have global identity. ENTITIES inside the boundary have local identity, unique only within the AGGREGATE. • Nothing outside the AGGREGATE boundary can hold a reference to anything inside, except to the root ENTITY. The root ENTITY can hand references to the internal ENTITIES to other objects, but those objects can only use them transiently, and may not hold onto the reference. The root may hand a copy of a value to another object, and it doesn’t matter what happens to it, since it’s just a value and no longer will have any association with the AGGREGATE
Thanks for the series. Informative content. I have a small question: Can I just use the aggregate pattern in Clean Architecture without using DDD (no events etc..).
Hello Milan, thank you for informative video. Quick question, how would you handle ForiegnKey relations with other aggregates inside? Should we use Keys or actual entities?
What if you want to display the available gathering types in the Ui before you create a gathering? I see a need to get all types without going through the aggregate root, right?
Gathering types being an enum, you can easily return them via some endpoint. Or even replicate the enum on the frontend (assuming it's a separate application)
I think this is where the concept of separation of commands and queries (CQRS) comes in handy. Querying the DB to read data for presenting in UI should not be handled by your aggregate repositories. You should instead use either raw SQL (executed via EF or Dapper), or create new query specification types for just that use case, and if you must use EF dbsets, I suggest you use a separate "read-only" dbcontext for your queries that power the views (MVC) or http GET API calls (web API). Then in this read-only ("AsNoTracking()") dbcontext, you can expose DbSets for all tables that you need to query directly, knowing you will never need a change tracker for this dbcontext as it is meant for presentation queries only. Only use your aggregates and repositories for use cases that modify data in the DB (aka, "commands"), because that's when you need the transactional consistency. Then you can limit your exposed DbSet properties on this DbContext, to only where T: AggregateRoot .
Milan, if the requirement is to delete an order item that belongs to the Order aggregate normally we would access the corresponding method from the aggregate, but in DDD are we considering this as an Update (whole aggregate) or DeleteOrderItem? What would be the implementation detail of it within aggregate root assuming that we know to persist this scenario against DB? In my head this is Update aggregate and would use Update method from the aggregate but also could be RemoveChildItem.
Hello Milan, thank you for those videos, I have this business need and I am a little bit confused about using aggregates: I have a step (that belongs to the destination aggregate) each step has a reference to a city. I have an address which reference also a city. the address exists in more than one table, should the address be in a separate aggregate? how to manage aggregate boundaries in this case?
Typically an address is a value object, but in your system it seems like it's an entity since you're working with destinations. From what you said - the address existing in multiple tables - implies you are using it as a value object. I'd be cautious with giving you advice from just your short description. Maybe you can show me some class diagram?
Is it really prohibited to fetch entities that fall under aggregate separately? I though that this rule applies only to write operations, whereas for read operations you can do whatever you want
Thanks for this great video! Great work! In case you need to load a gathering with the member information (Email, First Name, Last Name) of the attendees, will you hold a reference to the Member entity on the Attendee collection? Or do you think it is better to have another abstraction that represents the Member information on the Attendee entity? Or just having in the query handler a reference to the dbContext and constructing the query without loading aggregate?
Hi Milan, great video again. I just want to ask about refactoring legacy code with anemic domain model. I want to apply your teachings towards creating rich domain model but some of these concepts about aggregates are quite complicated so I think I need to spend more time practicing. Would it be a good first step to just move domain logic out of the handler method and into the domain model first without aggregate design and such? Thanks!
But what happens when you want to delete, say, an invitation? Does the client have to include both the gatheringid and the invitationid in the request? If so, does this mean you can’t create a restful api (where you send a delete method with just a single id)?
So, if an entity (that is mapping to a table) can be an aggregate by adding bussiness logic inside its class (rich model), then perform CRUD with that class to persist into DB ? The AggregateRoot, because of it empty, is only use for marking that some classes is an aggregate root ? When you say fetch aggregate with related entities, you changed the repository codes, why ? We still can use Include() method for the same purpose. I have a small design, see below: A class named User (Id, Name, List SkillList, List ProdList ), Skill class (Id, SkillName), Product (Id, ProdName). When i create an user, it will also add all products belong to current user, i am tending to create an aggregate named UserCreateAggregate that only has 2 required props (User user // root, List ProdList) with a void CreateMethod() { user = new(); user.prodList = Prodlist; }. I am stucking right here, i don't know how to persist to DB, i have 2 ways, feel free to express your opinion: 1. Pass UserRepo to Aggregate then saveChanges(), this way worked but it looks stupid :) 2. Return current user (use prop that is declared earlier) to service class then userRepo saves it to DB. (this seems more appropriate). Additional info, i am using EF Core with UOW and repository (For each entity).
Entities can contain business logic (rich models) and be used for CRUD. Return the user from your aggregate to the service, then save via repository. Consider simplifying your architecture - full UoW and repository patterns may be unnecessary with EF Core.
I'm a bit confused about how to define aggregates when there's a N:N relationship, imagine we have TaxRegimes and TaxPayerType, you may have a use case to where you create a TaxPayerType with some TaxRegimes, and other use case where you create a TaxRegime with some TaxPayerTypes associated with it. Which one is the aggregate root? TaxRegime or TaxPayer?
what if you have huge data like 100000000comments in Post object. I don't think you can init a domain object with that much... looks useless to me when dealing with large chuncks of data. Thoughts?
Hi Milan Thank you for your effort I was just confused about how the relationship between these tables will look according to this diagram there is a cyclical relationship between the tables the entity framework gives me this error Introducing FOREIGN KEY constraint 'FK_Invitations_Members_MemberId' on table 'Invitations' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints. Could not create constraint or index. See previous errors.
Say you have a very large gathering, like 2 million people... everyone one of these people accepts the invitation you're going be retrieving the entire aggregate with all invitations each time, just so that you can then access the single invitation you want to accept?
@@MilanJovanovicTech- So when you put this together (yes it's a year ago), what was your thought process behind grouping the attendee and invitation as part of an aggregate root of Gathering, rather than Member, because it seems pretty clear that a Gathering has the potential to have significantly more members, than a Member would attend gatherings... This thought process of domain design is what I'm interested in, and is rarely discussed.
You have been using same domain entity to persist changes in DB. Can you create video where both DB and domain entities are separate and mapped in Application layer? Because domain entities are not supposed to be dealt with DB even if DB and domain entities share the same properties .
Hey! Two questions: 1. What real problem does this solve beyond following the established artificial rule that logic should be in aggregate root? Apart from the change in the location of the function, nothing has changed. 2. Why not fetch the invitation along with the gathering in one query? Commands, after all, are not reusable.
You have to look at this from a conceptual perspective instead of focusing on a made-up example. The ideas I'm implementing here are that the Aggregate should act as a consistency boundary for the root and respective entities - and - that the aggregate root should expose methods to manipulate it. I talked about this more from a theoretical perspective here: th-cam.com/video/Pkvt87yL6Gs/w-d-xo.html
Want to master Clean Architecture? Go here: bit.ly/3PupkOJ
Want to unlock Modular Monoliths? Go here: bit.ly/3SXlzSt
Nice one man. It's really cool that someone explains such stuff to the people who are not familiar with this yet. I with there were such videos when I was investigating this topic
At least we have this for the future generations. Right? :)
@@MilanJovanovicTech definitely)
Great video! I have a couple of questions:
1. What if Gathering is having 1000+ Invitations? Is it wise to fetch the entire Aggregate?
2. When dispatching domain events before committing changes to the DB. How do we handle Email/NotificationService correctly if case committing afterwards fails?
1. I already mentioned in a few other comments 😅
One solution can be filtered include.
2. Coming up in next video 🕜
What is "afterwards" in your case?
@@MilanJovanovicTech
1. You mean that in retrieving the aggregate, I add a condition on what related data to include? .Include(gathering => gathering.Invitations.Where(x => x.DateSent < date)) ?
That would work!
2. My domain events are dispatched before persisting the changes to the DB. If you trigger sending an email. How would you handle that if the save fails? Sent a new e-mail? Perhaps acceptable in this case if requierments call for it.
I’m looking forward to your upcoming video!
@@MilanJovanovicTech I gave it a through. When pre-dispatching events (before saving changes), instead of calling the e-mailservice directly you put the action in an outbox that can be committed after successfully saving the changes, as with integration events. My worries is about how to make that clean.
I got my question answered so will delete my comment. Thank you both!!!,😊
Thanks Milan for this great series of DDD. Much appreciated your effort!
Thanks a lot. I'm quite enjoying making it! There is still a few more videos in the DDD series left 😁
was waiting for Aggregate Root Design thx so much
I hope I managed to convey the topic properly!
Wait for the next ep., very interesting topic
Dropping on Friday 😁
On first looking at the project, I would think that a Member sends an invitation. Understanding that the Gathering is sending the invitation (and managing the attendees) makes perfect sense but isn’t obvious to a newbie like me so thank you.
The question to ask in that case would be: "Who owns the invitation"?
Is the the Member?
Or the Gathering?
I decided on Gathering. You may come to a different conclusion. And we could both be right 😁
@@MilanJovanovicTech We model objects of real life so it is impossible for "gathering" to send invitations.....maybe this is responsibility of creator
Let's say, Post is the AggregateRoot and Comments are the children of Post. A post can have thousands of comments. How would you update a comment through the AggregateRoot? Obviously not pulling all the comments into memory just for updating a single comment. I think we should use filtered include for the children.
Filtered incldue definitely solve this problem, that's a great suggestion. But what I didn't cover in this video is that you have consider performance when designing Aggregates.
In your example, performance would be severely degraded if we used Post as the AggregateRoot.
The proper approach would be to have the Comment reference the Post by FK.
That way we can only fetch that Comment, and modify it.
We wouldn't have a "real" Aggregate,
This scenario maybe implemented by domain events. In post you may fire commentUpdated event and intercept this event to do the update.
The way I understand it is the aggregate root is meant to enforce invariants within the aggregate cluster and ensure they're accessed and modified in a consistent and controlled manner. The root protects the boundary around the aggregate cluster, which doesn't necessarily mean loading a ton of data just to modify bits of data.
Братан, хорош, давай, давай, вперёд! Контент в кайф, можно ещё? Вообще красавчик! Можно вот этого вот почаще?
Da
Hi Milan which book do you recommend to learn more about this topic?
Domain-Driven Design, Eric Evans
So choosing an aggregate is mainly based on responsibility of the class. What you can identify as a single process and you treat those objects as one unit?
There's a bit more nuance to it. But that's the general idea. It's hard to define a set of rules. It kind of evolves on it's own as you build out the project.
I got the idea to maintain consistency in DDD, but performance is still an issue if I pull all that information from database. Some others folks also pointed that out here.
Absolutely, DDD should never be followed to the letter. You must be *pragmatic*. If you have a collection with a tendency to grow extremely large: post-comments (Reddit). Then we can throw out this design out the window.
I'm really glad so many people are commenting about performance - it means I have good engineers watching these videos 😁
@@MilanJovanovicTech I think another way to reduce the complexity is what Eric Evans says about associations in the blue book. He says that there are at least three ways of making associations more tractable:
1) Imposing a traversal direction
2) Adding a qualifier, effectively reducing multiplicity
3) Eliminating nonessential associations
In that case I believe number 2 may help to reduce the number of "valid" invitations. But I would appreciate a Milan´s video about it haha
I wonder, lets say i am trying to model the problem of doing some business operations on historical stock data, so a stock is basically a symbol (for example AMZN), and a time series that represents the data of that stock. Often we want to query and do operations on the stock data based on symbols, so ultimately my question is, should symbol also be an aggregate root, alongside with stock? If not, should i still have two separate repositories? If so, should I create a repository that encapsulates both of them? How would you go about handling this situation
I'd choose the approach that's easier to work with in terms of:
- Storing data
- Querying data
Symbols are just like an enum value, right? They don't change much. You're more interested in the time series data of the stocks price changing over time.
@@MilanJovanovicTech I see, in my case, I want to allow the functionality of assigning tag to symbols, for example: S&P 500, bad stock, etc.
So I think it is more fitting that the symbols would also be aggregates, as they are more complicated than a simple string, and also, while it is true that they don't really change overtime, I don't want to assume they are always existing in my DB, as I'd assume all enum values are in an enum
Don't we need to specify parameterless private constructor to let Entity Framework create an object of specific Entity ? But we should pass an Id to the constructor of the base class using your approach. How do you handle that ? Do you use specific Entity within DbContext?
In most cases EF can get by with a constructor that has some params, like the one with the ID and use the properties to fill in the rest
Thanks Milan! Which tool do you use for model diagram?
draw.io
Hi, watching this a bit later :D
Any reason for not using SingleOrDefault on the gathering.Invitations?
Nothing in particular
If your only reading data, why is consistency and issue? Surely an aggregate is for only write actions, read actions should be allowed outside the aggregate if that functionality is necessary (I.e. you only care about a small subset of the aggregate data in some scenario)? If I want to list to the user all invitations for whatever reason, surely I shouldn’t have to pull that through the aggregate?
That's a query - and it's a separate concern from the Domain
@@MilanJovanovicTech ahhh I must have misunderstood the video I thought you was talking about both commands and queries
Hi Milan
in the case that we have to return a dto to the client with data of multiples aggregates, how we can do that without doing multiple queries to the db? because we cant retrieve that data in one single query because the aggregates are not related in the properties so we cant include that data
You just do multiple queries to the DB 🤷♂️
Great video. Could you explain us an alternative aproach to the case where has hundreds/thousands of Invitations, would you separate again?
Excellent question! Yes, definitely this does not hold in such a case. You couldn't have a collection so large in that case.
You would have to make Invitations reference Gathering, to make the solution peformant.
It's a lengthy discussion. I will try to find you some good reading resources, if there are any. Or just create a separate video on the topic.
I am trying to learn the aggregate root patterns but it seems hard for now. Can you please list down the tutorial links from beginning to intermediate so that we can follow them? For example in this video I can see u are adding Aggregate but what are value objects, Entity, how did u add them and which tutorial will show these things?
There's a playlist which you can follow on my channel and it goes over everything in order
Hi Milan, since Gathering is the aggregate root, I'm confused that Member knows about the Id of the Invitation. Components outside of the aggregate should not be allowed to reference Ids of non-root entities.
| Components outside of the aggregate should not be allowed to reference Ids of non-root entities.
Why not?
@@MilanJovanovicTech According to Evans: Now, to translate that conceptual AGGREGATE into the implementation, we need a set of rules to apply to all
transactions:
• The root ENTITY has global identity, and is ultimately responsible for checking invariants.
• Root ENTITIES have global identity. ENTITIES inside the boundary have local identity, unique only
within the AGGREGATE.
• Nothing outside the AGGREGATE boundary can hold a reference to anything inside, except to the root
ENTITY. The root ENTITY can hand references to the internal ENTITIES to other objects, but those objects
can only use them transiently, and may not hold onto the reference. The root may hand a copy of a
value to another object, and it doesn’t matter what happens to it, since it’s just a value and no longer
will have any association with the AGGREGATE
Thanks for the series. Informative content. I have a small question: Can I just use the aggregate pattern in Clean Architecture without using DDD (no events etc..).
Of course you can :)
Hello Milan, thank you for informative video. Quick question, how would you handle ForiegnKey relations with other aggregates inside? Should we use Keys or actual entities?
Aggregates should only reference other aggregates. So for sure use FK
What if you want to display the available gathering types in the Ui before you create a gathering? I see a need to get all types without going through the aggregate root, right?
Gathering types being an enum, you can easily return them via some endpoint. Or even replicate the enum on the frontend (assuming it's a separate application)
I think this is where the concept of separation of commands and queries (CQRS) comes in handy. Querying the DB to read data for presenting in UI should not be handled by your aggregate repositories. You should instead use either raw SQL (executed via EF or Dapper), or create new query specification types for just that use case, and if you must use EF dbsets, I suggest you use a separate "read-only" dbcontext for your queries that power the views (MVC) or http GET API calls (web API). Then in this read-only ("AsNoTracking()") dbcontext, you can expose DbSets for all tables that you need to query directly, knowing you will never need a change tracker for this dbcontext as it is meant for presentation queries only.
Only use your aggregates and repositories for use cases that modify data in the DB (aka, "commands"), because that's when you need the transactional consistency. Then you can limit your exposed DbSet properties on this DbContext, to only where T: AggregateRoot .
Great content! thank you Milan
I'm glad you liked it Fernando!
Milan, if the requirement is to delete an order item that belongs to the Order aggregate normally we would access the corresponding method from the aggregate, but in DDD are we considering this as an Update (whole aggregate) or DeleteOrderItem? What would be the implementation detail of it within aggregate root assuming that we know to persist this scenario against DB? In my head this is Update aggregate and would use Update method from the aggregate but also could be RemoveChildItem.
What does it change?
@@MilanJovanovicTech While doing remove an order item the action represents the aggregate change.
Hey Milan, great video! So this practically means you can manipulate the whole hierarchy only through the main object?
Yes, that is the idea behind the pattern. Designing a proper Aggregate is a science, to be honest. You can never get it quite right.
Great video
I'm very glad you liked it!
Hello Milan, thank you for those videos,
I have this business need and I am a little bit confused about using aggregates:
I have a step (that belongs to the destination aggregate) each step has a reference to a city.
I have an address which reference also a city.
the address exists in more than one table,
should the address be in a separate aggregate? how to manage aggregate boundaries in this case?
Typically an address is a value object, but in your system it seems like it's an entity since you're working with destinations.
From what you said - the address existing in multiple tables - implies you are using it as a value object.
I'd be cautious with giving you advice from just your short description. Maybe you can show me some class diagram?
@@MilanJovanovicTech I can't upload an image, where can i share the class diagram
@@mohammedessaddek8175 LinkedIn/Twitter
intersting video, thanks. What is the font in your IDE editor ?
Whatever Visual Studio default is
Is it really prohibited to fetch entities that fall under aggregate separately?
I though that this rule applies only to write operations, whereas for read operations you can do whatever you want
No, all rules are meant to be broken
Thanks for this great video! Great work!
In case you need to load a gathering with the member information (Email, First Name, Last Name) of the attendees, will you hold a reference to the Member entity on the Attendee collection? Or do you think it is better to have another abstraction that represents the Member information on the Attendee entity? Or just having in the query handler a reference to the dbContext and constructing the query without loading aggregate?
Plain and simple, if it gets the job done. So go with Member reference on Attendee
Are you offering any coupon for the course?
From time to time
Hi Milan, great video again. I just want to ask about refactoring legacy code with anemic domain model. I want to apply your teachings towards creating rich domain model but some of these concepts about aggregates are quite complicated so I think I need to spend more time practicing. Would it be a good first step to just move domain logic out of the handler method and into the domain model first without aggregate design and such? Thanks!
That would be a wonderful first step, yeah. Domain modeling doesn't have to be one-and-done. It's an evolution.
But what happens when you want to delete, say, an invitation? Does the client have to include both the gatheringid and the invitationid in the request? If so, does this mean you can’t create a restful api (where you send a delete method with just a single id)?
That depends what is RESTful to you? I think this is a good route:
DELETE api/gatherings/{gatheringId}/invitations/{invitationId}
@@MilanJovanovicTech that’s a good point
@@cmartinez4727 Not to say the other approach would be wrong.
There's more than one way to design this 😁
So, if an entity (that is mapping to a table) can be an aggregate by adding bussiness logic inside its class (rich model), then perform CRUD with that class to persist into DB ?
The AggregateRoot, because of it empty, is only use for marking that some classes is an aggregate root ?
When you say fetch aggregate with related entities, you changed the repository codes, why ? We still can use Include() method for the same purpose.
I have a small design, see below:
A class named User (Id, Name, List SkillList, List ProdList ), Skill class (Id, SkillName), Product (Id, ProdName).
When i create an user, it will also add all products belong to current user, i am tending to create an aggregate named UserCreateAggregate that only has 2 required props (User user // root, List ProdList) with a void CreateMethod()
{
user = new();
user.prodList = Prodlist;
}.
I am stucking right here, i don't know how to persist to DB, i have 2 ways, feel free to express your opinion:
1. Pass UserRepo to Aggregate then saveChanges(), this way worked but it looks stupid :)
2. Return current user (use prop that is declared earlier) to service class then userRepo saves it to DB. (this seems more appropriate).
Additional info, i am using EF Core with UOW and repository (For each entity).
Entities can contain business logic (rich models) and be used for CRUD. Return the user from your aggregate to the service, then save via repository. Consider simplifying your architecture - full UoW and repository patterns may be unnecessary with EF Core.
Milan first of all love your work
You would do it differently if the collection is large. Just load a single invitation, and have it reference a Gathering.
I'm a bit confused about how to define aggregates when there's a N:N relationship, imagine we have TaxRegimes and TaxPayerType, you may have a use case to where you create a TaxPayerType with some TaxRegimes, and other use case where you create a TaxRegime with some TaxPayerTypes associated with it. Which one is the aggregate root? TaxRegime or TaxPayer?
What if we create a larger concept to hold both of them together?
Thanks
You're welcome
can you create a tutorial where you create a project using ddd and cqrs
I have an entire series about that on this channel: th-cam.com/video/tLk4pZZtiDY/w-d-xo.html
Hello over-fetching, my old friend
I've come to talk with you again
what if you have huge data like 100000000comments in Post object. I don't think you can init a domain object with that much... looks useless to me when dealing with large chuncks of data. Thoughts?
The amount of data also dictates domain modeling, obviously. We wouldn't create a collection navigation in that case.
please i went to your app and i did not find the source codes
It's all shared on Patreon: www.patreon.com/milanjovanovic
Hi Milan, can you upload the code to GitHub so it is easier to read?
Hi Krešimir, I have a similar example on my GitHub already that you can take a look at.
github.com/m-jovanovic
@@MilanJovanovicTech Thanks for all you do. This one was especially valuable.
Is this a rule, not to use multiple repositories in the handler? there is still attendeeRepository there
Not a rule, no
Hi Milan Thank you for your effort I was just confused about how the relationship between these tables will look according to this diagram there is a cyclical relationship between the tables the entity framework gives me this error
Introducing FOREIGN KEY constraint 'FK_Invitations_Members_MemberId' on table 'Invitations' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
Could not create constraint or index. See previous errors.
You can define a cascade behavior you think is appropriate in EF config
Say you have a very large gathering, like 2 million people... everyone one of these people accepts the invitation you're going be retrieving the entire aggregate with all invitations each time, just so that you can then access the single invitation you want to accept?
I hope it's obvious that we change the domain model based on the application load and requirements. Right?
@@MilanJovanovicTech- So when you put this together (yes it's a year ago), what was your thought process behind grouping the attendee and invitation as part of an aggregate root of Gathering, rather than Member, because it seems pretty clear that a Gathering has the potential to have significantly more members, than a Member would attend gatherings... This thought process of domain design is what I'm interested in, and is rarely discussed.
@@MilanJovanovicTechyou do so wrong. Sounds very impressive. Your analysis is so wrong though
You have been using same domain entity to persist changes in DB. Can you create video where both DB and domain entities are separate and mapped in Application layer? Because domain entities are not supposed to be dealt with DB even if DB and domain entities share the same properties .
Is there a value in having a separate persistence model?
@@MilanJovanovicTech why not, when we are having such a pain of declaring domain entities, separate out every layer then why not entities?
I just failed at coding round about this concept. but thanks anyway
That sucks. Who asks DDD questions in an interview, though?
how to "search everywhere"?
What?
@@MilanJovanovicTech at 2:48 of the video, you open a search box, how - with what command - did you do that?
@@danielmoreira4981 Ctrl + T
Let's forget about ubiquitous language for the time being then.
Ok
Aggregate should not contain Invitations, but InvitationIDs
All right
Wrong
What's right then, shane?
Hey! Two questions:
1. What real problem does this solve beyond following the established artificial rule that logic should be in aggregate root?
Apart from the change in the location of the function, nothing has changed.
2. Why not fetch the invitation along with the gathering in one query?
Commands, after all, are not reusable.
You have to look at this from a conceptual perspective instead of focusing on a made-up example.
The ideas I'm implementing here are that the Aggregate should act as a consistency boundary for the root and respective entities - and - that the aggregate root should expose methods to manipulate it. I talked about this more from a theoretical perspective here: th-cam.com/video/Pkvt87yL6Gs/w-d-xo.html