Тёмный

Aggregate (Root) Design: Separate Behavior & Data for Persistence 

CodeOpinion
Подписаться 87 тыс.
Просмотров 64 тыс.
50% 1

Опубликовано:

 

14 окт 2024

Поделиться:

Ссылка:

Скачать:

Готовим ссылку...

Добавить в:

Мой плейлист
Посмотреть позже
Комментарии : 171   
@MilanJovanovicTech
@MilanJovanovicTech 3 года назад
So didn't we just end up with an anemic domain model? Which is one of the things that DDD is supposed to solve in the first place. On another note, I do think that you can handle complex domain models with EF Core. I personally didn't have too much trouble with mapping private fields or private owned types etc.
@CodeOpinion
@CodeOpinion 3 года назад
No, your aggregate contains all behavior, but your encapsulating your data models within it. Your not having application services interact with the data directly. As for EF Core, I suspect your right and it's easier then it used to be.
@hamlet.h.hakobyan
@hamlet.h.hakobyan 3 месяца назад
@@CodeOpinion @MilanJovanovicTech I'm wondering, is it ok to have domain layer to depends on something, particularly to infrastructure?
@osman3404
@osman3404 3 года назад
I like how the Save method was just processing events and actually will work will with EF change tracking as well
@jannekallunki154
@jannekallunki154 3 года назад
EF Core "things you must have" in entities is getting smaller and smaller. With fluent configuration it's hard to tell if EF Core was used by looking just the entities. Great video anyway!
@dbpieter
@dbpieter 3 года назад
I've been using MongoDb for my latest project. Just being able to build your aggregate model without any restrictions caused by ORM/SQL/EF has been so liberating. This advice is great though ! Will definitely look into this approach if I'm ever (forced) to go back to sql for persisting aggregates.
@EricLouisYoung
@EricLouisYoung 3 года назад
Same here. One of the best tech decisions I've ever made.
@stevehoff
@stevehoff 2 года назад
Same boat.
@CottidaeSEA
@CottidaeSEA Год назад
I have a few issues with NoSQL. 1. I usually know whatever structure I have. 2. SQL usually allows for JSON columns that provide similar functionality. 3. It's less performant; usually not an issue, but it's not a problem until it is. 4. It doesn't improve my productivity. It is very liberating to use however.
@adrielairaldo
@adrielairaldo Год назад
A document database works well until you have relationships between the entities that are within your aggregates (child entities). In that case, when you update a child entity your aggregate is left in an inconsistent state on one side of the relationship. And that's where the nightmare begins... that doesn't happen with a relational database.
@CottidaeSEA
@CottidaeSEA Год назад
@@adrielairaldo Then it's better to have data duplication with message queues. Although that kind of sucks too.
@arielunanue4354
@arielunanue4354 Год назад
Great video. Perhaps exposing the "Items" property as IReadOnlyCollection to prevent modifications to the Items list outside the aggregate root.
@romanlunin386
@romanlunin386 2 года назад
The best youtube channel ever, thanks Derek!
@CodeOpinion
@CodeOpinion 2 года назад
Thanks for watching!
@offthepathworks9171
@offthepathworks9171 Год назад
Pretty cool example, simple to follow, great for us new to the architecture.
@kylegivler8372
@kylegivler8372 11 месяцев назад
I really appreciate your explanation of an aggregate.
@Codewrinkles
@Codewrinkles 2 года назад
I like the approach you propose here, but at least in my experience and based on other readings (Eric Evans, Martin Fowler ecc) I think that you start from a wrong premise. The reason why I want to have a private constructor is not to "conform" to EF, but to be able to instantiate an object only in a public static factory method that I expose. The reason we use backing fields for Lists is once again not to conform to EF, but to just not allow consumers to manipulate the collection except through a public behavior method that I expose. Further, using configuration classes for EF you can totally configure the way you want EF to persist that data, without the domain model to even care about that.
@CodeOpinion
@CodeOpinion 2 года назад
Thanks for the comment. Put another way, what I'm suggesting is having a clear separation between data models and behavior models (aggregate root). The vast majority of folks try want to make an EF Model be both and then have to make compromises or configuration to do so. My argument is just accept they are different concerns.
@brendonvandoornum6123
@brendonvandoornum6123 3 года назад
One of the things that could be missing from the repository layer is the unit of work pattern. In more complex scenarios, there may be the need to use two or more repositories that shared the same context or transaction. From what I have seen, this means you move the SaveChanges from the repository and call _unitOfWork.SaveChanges() in the application layer. What are your thoughts on the unit of work pattern for repositories and do you think multiple repositories should share the same transaction?
@CodeOpinion
@CodeOpinion 3 года назад
I have used the Unit of Work pattern in the (long distant) past, but have not have much of a need for it. I'm not saying it's not valuable/useful, but I've had limited need for it. This likely is because of how boundaries are defined and viewing an aggregate as a consistency boundary. I plan on doing a video about this in the near future.
@brendonvandoornum6123
@brendonvandoornum6123 3 года назад
@@CodeOpinion I would really like to see that video, finding the right boundaries is one of the hardest things to do. Keep up the good work
@mattiasandersson9825
@mattiasandersson9825 2 года назад
@@CodeOpinion Yes pretty please, do that video.🙏
@professional_programmer
@professional_programmer 10 месяцев назад
DbContext implements the Unit Of Work pattern. No reason to implement a UoW on top of another...
@aivarasatkocaitis4356
@aivarasatkocaitis4356 3 года назад
Hey, nice video, I couldn't find a link to source code used for this example, is it available somewhere?
@ahmedeox
@ahmedeox 9 месяцев назад
I haven't watched the video yet, but i'll just lay out the way I do it. I just make an Adapter class which simply converts from domain objects into persistence objects and then invokes the repository methods. I do something similar with the inputs/outputs from the front/end. I have to say that it is really liberating not having to deal with persistence issues.
@jamesalcaraz8729
@jamesalcaraz8729 3 года назад
Assuming that I have a separate project for my Domain (containing the aggregate) and infrastructure (containing the db context and the ef models). Passing the EF model to the Aggregate means that the domain project must have a reference to the infrastructure project. Am I missing anything or are you suggesting to put the EF models in the Domain project?
@CodeOpinion
@CodeOpinion 3 года назад
Yes. Or put your data models with your domain. Or put them separately to both. Food for thought, if you were/are emitting events from your domain, where would those live?
@jamesalcaraz8729
@jamesalcaraz8729 3 года назад
@@CodeOpinion Putting your data models with your domain means you'll have reference to EF in your domain, because of the annotations that you'll use in your data models. You can't really have a common project otherwise you'll have a circular dependency. Infra references Domain, Domain and Infra both references "Common". Not trying to rain on your otherwise great content, just want to understand if I can apply this to my upcoming project.
@CodeOpinion
@CodeOpinion 3 года назад
It's a good topic and hard to explain just via comments. If you're trying to apply clean/onion architecture, I get it. I'm not against it at all, I don't use it as prescribed. However I think where I differ in opinion is what concerns belong to which layer.
@CodeOpinion
@CodeOpinion 3 года назад
I think I need to make a video about this to explain more. Stay tuned for that.
@petertran141
@petertran141 3 года назад
@@jamesalcaraz8729 you can use Fluent API instead of annotations. so put your data models under Domain and write the EntityConfiguration in Infrastructure.
3 года назад
As a Java - Springboot guy, I find this very useful. Nice work!
@CodeOpinion
@CodeOpinion 3 года назад
Thanks! My hope is that even though the examples are in C#, most people can probably understand it and infer how it would be in the language they use.
@kevinof1978
@kevinof1978 3 года назад
Derek, I've been thinking about your `Save` method in the events example repository, and that you normally only have `Get` and `Save` methods -- the adding and removal operations were not covered. I imagine you could either provide `Add` and `Remove` methods in the repository, or treat creation and deletion as events recorded in the domain object. How do you prefer to handle creating and remove new aggregates when using events to track changes?
@CodeOpinion
@CodeOpinion 3 года назад
Adding & Removing are state changes like any other. If you "create" a new aggregate, it's just the first event. eg OrderCreated and would have the corresponding statement in the repository to insert the record. Same goes for deleting. Make sense?
@kostasgkoutis8534
@kostasgkoutis8534 Год назад
@@CodeOpinion Not quite, because the Save() method in your EFCore example doesn't get the shoppingCart as argument (as it does with Dapper). I would think something like the following: public class ShoppingCartDomain { private readonly ShoppingCart _shoppingCart; private ShoppingCartDomain(ShoppingCart shoppingCart) // now private { _shoppingCart = shoppingCart; } //.... rest public static ShoppingCartDomain CreateOrder(Guid shoppingCartId, Guid customerId, Action completeOrderCreation = default) { ShoppingCart shoppingCart = new ShoppingCart(shoppingCartId, customerId); ShoppingCartDomain shoppingCartDomain = new ShoppingCartDomain(shoppingCart); completeOrderCreation(shoppingCart); } public void CancelOrder(Action completeOrderCancellation = default) { completeOrderCancellation(_shoppingCart); } } This way you can pass as argument something like this (ShoppingCart shoppingCart) => _dbContext.Add(shoppingCart); or _dbContext.Remove. Not sure how much more I like it though in comparison to the approach you showed with Dapper.
@Imploser
@Imploser 2 года назад
I prefer doing this since i design my first DDD project. Because, aggregates and value objects could have transient properties. Also to my opinion, ORM dependency on aggregate is wrong. Only problem when i doing this is dispatching domain events to other domains. I try 2 different ways for that: 1- Command-Query Domain Services: An interceptor of CommandService which returning the aggregate dispatches after CommandService returning. 2- A component for aggregate db command operations which has insert, update, delete methods wraps Repository and AggregateToEntityConverter. Also, it has interceptor for domain event dispatching. Lastly; I am using this one with application services, without domain services.
@dgodiegomartins
@dgodiegomartins 2 года назад
Great Content. Hi from Brazil! If i need to persist de entire basket in a redis cache for example. Do I need to expose a getter for access de basket model inside the Repo?
@korayenko
@korayenko Год назад
I was looking for that, thanks.
@Cleannetcode
@Cleannetcode 3 года назад
I really love your code opinion :) Absolutely agree with you!
@CodeOpinion
@CodeOpinion 3 года назад
Glad to hear it!
@Imp0ssibleBG
@Imp0ssibleBG 2 года назад
I find this part "And if you're not using an ORM, what's a way that you can capture the changes made by the Aggregate Root so that you can persist them writing SQL?" a bit unclear. I use go, but I guess the point still stands for C#. I am not using an ORM (have my own store/repository) and the way I persist data is not by using events, but simply dependency injecting the store in my service and for example In my UserService I have method which modifies the user and saves it using the injected repository. How and why events matter to me (according to your example in the video)?
@CodeOpinion
@CodeOpinion 2 года назад
What type of database are you using? If you're using a relational database then you have to be writing SQL statements (insert/update/delete). Your repository I assume then is taking the entire entities and doing full update/insert/delete statements for each entity? The purpose of events in my example is to manually implement change tracking. Having explicit events of things that occur that you can then have specific SQL statements to persist those changes. The alternative would be to write full UPDATE statements regardless of what changed, but for the entire aggregate (even if nothing changed, you wouldn't know).
@Imp0ssibleBG
@Imp0ssibleBG 2 года назад
@@CodeOpinion I have CQRS implemented, for example I have a product and I can call the command to adjust the product quantity - I am not passing the whole product just the identifier and the changed value. I make the corresponding call to that but I am not using events for this, it is all coming from my ports (https)
@mfornarisc
@mfornarisc 3 года назад
It would be good if you pair those videos with the code you used to explain the architecture or pattern. Do you have the project you used in this video?
@TheCEMF
@TheCEMF 8 месяцев назад
Is Guid always the way to go when you approach DDD? I'm thinking about applying DDD to an already existing system, however, I came across some frustrating situations where the id is incremental, and I cannot fathom to use an incremental id without breaking the invariance inside my aggregates... :(
@sampsonorson
@sampsonorson 3 года назад
Great content as usual. Please, how about when you need to return the list of shopping carts items to the client with price. How would you handle that?
@CodeOpinion
@CodeOpinion 3 года назад
I wouldn't use an aggregate for queries. Rather I'd go directly to the source to get the data I need. In other words, I generally don't use a repository or an aggregate for queries, only for commands.
@sampsonorson
@sampsonorson 3 года назад
@@CodeOpinion Thanks for your reply. When you say source, what do you mean? Do you mean the DbContext? Just want to be sure. Thanks.
@ThugLifeModafocah
@ThugLifeModafocah Год назад
I believe that there is something fundamentally wrong with most, including this one, DDD implementations. For example, Evans says that a repository should be an abstraction of the persistence layer inside the domain and should behave as a regular object, not exposing persistency details in domain. So, where are you using the repository inside the domain? Shouldn't inside the domain you have, for example, in the method AddItem, the repository interface being called to already persist the data?
@ttst3
@ttst3 Год назад
Yes, this is my problem with the video as well. It seems the domain here is using the ORM, which is completely opposite of what Evans suggested.
@yevheniikurtov5990
@yevheniikurtov5990 2 года назад
It's an interesting approach to have only ‘get’ and ‘save’ methods on aggregates. What do you do when you need to look up an aggregate by a secondary index?
@CodeOpinion
@CodeOpinion 2 года назад
Doesn't really happen that often tbh. I guess context specific but if you need to, expose that Get in away to retrieve the relevant data
@jcmoore2013
@jcmoore2013 Год назад
Dont use EF, just use repositories. Do you have any other videos on handling updatibg and saving. In addition, with this example how would the repo handle when the domain is seperated in a different layer with clean architecture.
@magnuspersson7447
@magnuspersson7447 Год назад
At around 6:47 we are introduced to the AddItem method. While I understand that the quantity is provided by the client, where does the price come from? I'm somewhat assuming that we don't trust user provided data so we somehow need to load the product, or atlest the price. How is this accomplished?
@rzaip
@rzaip Год назад
What about delete/add? Should that go through an aggregate domain model or do you use DbContext right away?
@VilsonFabricio
@VilsonFabricio 3 года назад
Your example of using events to track changes ends up very similar to how side effects are handled in a functional language like Haskell or Scala. The actions that need to be taken are expressed declaratively and reified as data (like your list of events) and then a separate interpreter (like your Save method), dispatches on them and executes the actual effects. Your approach also has the nice side effect (pun intended) that your AddItem method is now basically pure. Not fully so, since it expresses its logical return value by mutating _events, instead of actually returning it, but it is pretty close. In particular, it doesn't update the database and is therefore much easier to test. There is nothing to mock, just running assertions on the events is enough. Again, similar to functional programming. It is fascinating how it is possible to arrive at similar solutions by such different routes.
@CodeOpinion
@CodeOpinion 3 года назад
Thanks for the comment. Without using a functional first language, I'm often using concepts that are mostly used in functional languages or so I'm told.
@alfonsdeda8912
@alfonsdeda8912 2 года назад
Hi Derek, i have two questions: -is okay to pass parameters to repository constructor? -how should I manage repository for 3 classes that are related, every one has a list of other two classes, should I do 1 common interface with common properties and in every class add list of other two classes? But I think that I would create circular dependency. Thank in advance.
@CodeOpinion
@CodeOpinion 2 года назад
Not sure why there would be anything wrong with passing anything to the ctor? Not sure what you mean by the second question. A repository (to me) is about retrieving and persisting an aggregate as described in the video. If you have aggregates relating to each other, they are a consistency boundary and would be persisted independently. Sorry, not really sure what your question is.
@alfonsdeda8912
@alfonsdeda8912 2 года назад
@@CodeOpinion Thanks for your fast response! I mean that I have 3 classes almost identical except the relations with each other, should I create a repository for each of those or one from generic interface that implement each of those?
@CodeOpinion
@CodeOpinion 2 года назад
@@alfonsdeda8912 I'm in-different. It really depends on the system and what they are and how they use. Without really know a more I can't say.
@alfonsdeda8912
@alfonsdeda8912 2 года назад
@@CodeOpinion Thank you very much. I would have another question not related with repositories: - i want to have 30 background works that run continuously and simultaneously, should I use task.run or another method? Thanks in advance.
@UniXoiD69
@UniXoiD69 2 года назад
Thank you! very nice explanations! It looks like the second solution with events is more clear and totally separated from any technology. 1. When load is not so high and aggregate is small too, can we just update\insert everything without events? I think yes. We can delete from table where id not in our child list, insert entities without ids (if we use nullable ids), and update any with not null id. All properties should be public or use memento pattern to hide them from client code. Better with optimistic lock on aggregate root. 2. We can fetch aggregate root again just before saving and check "dirty" properties, save only what needed to save. With this way ids can be not null. 3. Try to implement UoW and remember the initial state of AR, then dirty check anything. 4. We can just use nosql db, it's a trade off, then we have problems with schema migration? Any thoughts why should we use sql instead of nosql?
@fly_robingg9165
@fly_robingg9165 3 года назад
Don't you lose persistence ignorance in your domain model, which is one of EF Cores core features? By taking in the ShoppingCart data model you need a reference to the DAL or am I missing something?
@CodeOpinion
@CodeOpinion 3 года назад
Touchy subject on persistence ignorance. It depends if you follow a layered/onion approach and how you view the EF entities. I'd argue that if you do follow onion and you have more complex models, then you're likely going down a road that your making concessions for your domain model because of the data model in EF. Also, if your EF Entity is just a POCO why can't it live along side the aggregate in the domain project? Your repository will still live in your data layer which is actually doing the query/persistence.. your EF entity isn't doing anything other than holding data.
@heymega4779
@heymega4779 3 года назад
These designs seem to be in favour of partially updating the aggregates instead of saving them as a single unit, which is odd. With aggregate roots, you would typically save everything (including the stuff that hasn't changed) to protect your invariants.
@CodeOpinion
@CodeOpinion 3 года назад
If you're using EF, it doesn't update properties you haven't changed. Meaning, the UPDATE statement it generates doesn't include the columns/values that haven't been changed from when it was retrieved. If you're more leaning towards concurrency, that's topic :)
@gyanookharel7440
@gyanookharel7440 Год назад
@@CodeOpinion it does generate query including the unchanged column in the latest EF Core 6 (using SQL server as a provider)
@thatojadezweni
@thatojadezweni Год назад
@@gyanookharel7440 did you by any chance disable tracking for that query then call the update method manually ?
@yurimelo3404
@yurimelo3404 Год назад
I did get confused. You said that aggregates shoudn't get aggregates roots back to do reads or queries. But you also said that your repository would have two method: Get and Save. But the GETmethod shouldn't be to do reads or queries?
@CodeOpinion
@CodeOpinion Год назад
You need to call Get so you return the aggregate, call some method/behavior, then call Save(). When referencing for query purposes, there really isn't anyway to get the data out of the aggregate itself, it just exposes methods to change state.
@yurimelo3404
@yurimelo3404 Год назад
@@CodeOpinion I got it! Thank you!
@Eamo-21
@Eamo-21 3 года назад
Really great videos, keep it up.
@CodeOpinion
@CodeOpinion 3 года назад
Thanks, will do!
@vadimprudnikov9072
@vadimprudnikov9072 3 года назад
This is interesting and imho working approach however, I feel a bit confused about the dependency from domain to the infrastructure stuff... I can imagine that writing tests will be a bit painful (at least requiring extensive use of mocks)
@CodeOpinion
@CodeOpinion 3 года назад
Writing tests to me doesn't change much. For the last version using events, check out this video as an example: ru-vid.com/video/%D0%B2%D0%B8%D0%B4%D0%B5%D0%BE-rGlNhYOqKZk.html
@leonardomangano6861
@leonardomangano6861 2 года назад
I found that using events to track changes inside an aggregate for persistence doesn't scale very well. I think a better way to do it is using immutable aggregates and doing a diff inside the repository to calculate what should we change in the DB. What do you think?
@CodeOpinion
@CodeOpinion 2 года назад
Sure, however you want to do change tracking ultimately is what it comes down to.
@henrikbolte3184
@henrikbolte3184 3 года назад
Doesn't your repository's save method violate Open/Closed Principle? For every new Event that might be added in the future you would need to modify your Save method.
@CodeOpinion
@CodeOpinion 3 года назад
Sure does. If that matters to you, you could use reflection to call the appropriate method for each event. However, for example purposes, that magic would be lost for the viewer..
@osman3404
@osman3404 3 года назад
Where does the UI get its data from? From the ShoppingCart entity? From the ShoppingCartDomain or from a ViewModel type data returned by the Repo? Also if the UI is a native app like WPF running on a different a tier but IS interacting with the ShoppingCartDomain, would you move the ShoppingCartDomain to the client tier or is it better to keep it in the backend and expose its Cart behavior via some APIs?
@CodeOpinion
@CodeOpinion 3 года назад
Generally, I do not use a domain or aggregate for the query side. But rather just query the database in the simplest way possible. How you do that depends largely on the app and how it's deployed. Creating an API or directly from the client, totally depends on the context of how the app is used, deployed, security, etc.
@void_star_void
@void_star_void 3 года назад
Quick question Why are you considering a save method for your repo? Isn't the role of a repo is to act as a collection for the aggregate? native collections do not have a save method and adding an object to a collection means saving it. If the reason we have the save method on our repo is to get identity generation and persist the aggregate then aren't we in a position of mixing responsibilities?
@CodeOpinion
@CodeOpinion 3 года назад
I'm not really sure what you mean. The repo has nothing to do wit Identity generation. The repo is simply to retrieve and persist an aggregate.
@mehdihadeli
@mehdihadeli 3 года назад
Hi @CodeOpinion, Why do you separate Domain class from ef model? Is it not extra work?
@CodeOpinion
@CodeOpinion 3 года назад
The example was just to illustrate that you don't need your domain model that has behavior to also be your EF data model. You can if you want them to be the same, but if it becomes difficult, then just separate the two.
@mehdihadeli
@mehdihadeli 3 года назад
@@CodeOpinion Yes, of course, In some cases, it will be helpful. Thanks
@andriyroman5422
@andriyroman5422 3 года назад
The sample with micro ORM is not really live ready, you have 2 separated things here 1) data retrieval of "Repository" and 2) data processing of Domain Events. This means that you totally rely that "Repository" retrieves (Joins, Aggregates) data in a perfect way for Domain Events. I believe that this is good thoughts for implementation but current state is uncontrolled for obvious gaps. For a meanwhile approach looks quite adoptive.
@CodeOpinion
@CodeOpinion 3 года назад
Not sure what you mean by the repository needs to retrieve data in a perfect way for domain events?
@andriyroman5422
@andriyroman5422 3 года назад
​@@CodeOpinion that means that usually entities are hierarchical and aggregates other entities, and in this case Domain module relies that those aggregated entities are actually there
@CodeOpinion
@CodeOpinion 3 года назад
Unfortunately, I'm still not following. Yes Aggregates are exactly that a hierarchical of entities. Not sure about the rest of your point "Domain module relies that those aggregated entities are actually there". Just not following that sorry.
@andriyroman5422
@andriyroman5422 3 года назад
@@CodeOpinion Lets assume we are building Repository for "User" entity and separately we have "UserDomain", in case user domains requires "User" entity to contain "User.Permissions" to perform some business logic, and Repository does not include this aggregated entity to the "User" entity, we end up with exception. Does that sound reasonable for you?
@MrSoloDev
@MrSoloDev 2 года назад
@@andriyroman5422 I also don't understand what you're saying :D
@RadoslawSzymanek-p9m
@RadoslawSzymanek-p9m 10 месяцев назад
Nice video. The only that made me cringe is when I saw List being used instead of Set. Since list is being used finding if an item is already in the shipping cart will take O(n). Could be important for shopping carts with a lot of items.
@vedantkoditkar5968
@vedantkoditkar5968 3 года назад
Thank god someone is talking some sense. I see people are so stuck with EF that they forget Domain was born before EF and will live forever even if EF cease to exists. This is the first video I saw from you and I've subscribe to your channel.
@CodeOpinion
@CodeOpinion 3 года назад
Thanks! Appreciate the sub.
@alancastro383
@alancastro383 3 года назад
Very nice! I've been working with azure table storage and was having some hard time applying some DDD tecniques. Can you share this code on github? Thanks!
@CodeOpinion
@CodeOpinion 3 года назад
Yup, I'll re-comment with the link when it's up
@OllyWood688
@OllyWood688 3 года назад
Members get the slides too :)
@rafakrasowski715
@rafakrasowski715 2 года назад
The only thing to take in mind is. Azure table storage, supports transaction only within one partition key.
@PelFox
@PelFox 3 года назад
Interesting approach with a private field. Though, how do you map that private field to a view model/dto for your consumer? If repository returns a Domain model with only exposed methods and the model itself is private, where and how do you project your client code? I like the idea, but having a private constructor for your EF core model seems like a small price to avoid having to make a wrapper or seperate data model.
@CodeOpinion
@CodeOpinion 3 года назад
I generally only use a repository and aggregates for writes, not reads. Reads/queries just get the specific data you need, not the entire aggregate. Aggregates are about invariants (for writes). Check out my video of that ru-vid.com/video/%D0%B2%D0%B8%D0%B4%D0%B5%D0%BE-64ngP-aUYPc.html
@PelFox
@PelFox 3 года назад
@@CodeOpinion Does that depend if you use NoSql or Sql though. I prefer to store and retrieve complete aggregates when working with NoSql. I usually go with CQRS. But in this example you would create an IReadRepository that provides the projected models right away? Otherwise to get the query you need you would have to leak a IQueryable or make a repository that returns different types of Dtos.
@CodeOpinion
@CodeOpinion 3 года назад
If you want to abstract data access, then sure. However you abstract, I prefer only getting the data I need to return to client in whatever model.
@PelFox
@PelFox 3 года назад
@@CodeOpinion Interesting with a repository for writes seperated from reads. If you do the approach where you wrap the datamodel inside the domain, from where do you retrieve this domain model with the datamodel inside it? Do you have a service that returns this, since the read repository queries for what you need and not returning aggregates?
@idian3206
@idian3206 3 года назад
Can I say that a root aggregate is mainly used to bound all entity that changes together(to keep everything in an invariant state)? and perform a query to one of the nested aggregate, we will have to implement a viewmodel or denormalised view for it, Like a cqrs. Did i get it correctly?
@CodeOpinion
@CodeOpinion 3 года назад
Yes. I generally don't expose any query methods on an aggregate and use separate path for querying. Your query path can ultimately use the same data source as your command path.
@idian3206
@idian3206 3 года назад
@@CodeOpinion I think I got you, by 'same data source' means that it is not always necessary to duplicate the data to additional hardware like elasticsearch or denormalized table, but a different read model to query data by means of joining is also suffice?
@brucewayne2480
@brucewayne2480 3 года назад
Thanks for this video. I'm a Js/Ts developper and struggling with user session. Can I consider user session as an aggregate of user and shopping cart that has it's own behaviour and data model ? I have an app like eventBrite so when a user loads a page I get it's sessionId and then load the userId and evenrId then load each entity into an aggregate , is this a correct approach ?
@CodeOpinion
@CodeOpinion 3 года назад
Check out the video I posted about design aggregates around invariants. That might help. ru-vid.com/video/%D0%B2%D0%B8%D0%B4%D0%B5%D0%BE-64ngP-aUYPc.html
@brucewayne2480
@brucewayne2480 3 года назад
@@CodeOpinion Thank you !
@avilin2213
@avilin2213 3 года назад
Good video, but I am confused that since there is ShoppimtCartItems, why not just pass it to repository and save the result. Use the event list seems more complicated
@CodeOpinion
@CodeOpinion 3 года назад
When not using an ORM, you need change tracking. An ORM is what is determining the delta between what it pulled from the data store and what you changed on the object(s). Those deltas are turned into SQL. If you don't have change tracking because you're not using an ORM, then you need to do this change tracking manually. This can be accomplished by using events. Hope that answers your question!
@jensingels5958
@jensingels5958 3 года назад
Should work for microservices but wouldn't this break for N-tier architecture? Entity Framework contains a unit of work with repositories but last time I checked you will still depend on Entity Framework if you don't include an EntityRepository for your entities & possible an EntityUnitOfWork if needed. So if you have an interface of the repository in your domain wouldn't you just overengineer the entire thing if you include another domainRepository on top of that? It would be odd to have DomainClass implementations in your dataInfrastructure.
@CodeOpinion
@CodeOpinion 3 года назад
Sorry, not sure I'm following when you say include another domain repository? Yes, EF implements repository, unit of work, and specification.
@br3nto
@br3nto 2 года назад
In the second example, the item quantities aren’t returned when getting a shopping cart. Does this mean that a different shopping cart aggregate root would be used for presenting the items and quantities of an existing cart from the one shown in the vid to capture the changes/events?
@CodeOpinion
@CodeOpinion 2 года назад
Second example being the domain model example where the data model is being loaded into it? Not entirely sure what you're referring to.
@br3nto
@br3nto 2 года назад
@@CodeOpinion yes. How is the data persisted in the second example? What does the schema look like for a relational database in the second example?
@CodeOpinion
@CodeOpinion 2 года назад
@@br3nto The same as the first. The Shopping cart data model which is attached/tracked to EF is as passed into the ShoppingCartDomain
@br3nto
@br3nto 2 года назад
@@CodeOpinion sorry, I was getting confused with a different comment on a different vid. Towards the end of this vid you say you don’t need the price or the quantity of the items in the cart because they aren’t needed to implement the logic. So I’m inferring from that this means there’s a separate aggregate root that does contain the shopping cart with items and price, but it’s different from this aggregate root.
@thedacian123
@thedacian123 11 месяцев назад
Where are you going to keep entity framework entities in infra layer ,not domain layer,are not we?
@botyironcastle
@botyironcastle 5 месяцев назад
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?
@matthewrossee
@matthewrossee Год назад
Hey, in ~2:55 you are passing entity framework entity to a domain model constructor. How does it apply to clean architecture, where domain project shouldn't reference any other layer?
@CodeOpinion
@CodeOpinion Год назад
Don't. Put your data models with your domain model.
@matthewrossee
@matthewrossee Год назад
@@CodeOpinion Okay, but then instrastructure layer still needs to reference domain, instead of just application layer. I don't understand that part.
@michelsafi1021
@michelsafi1021 2 года назад
How do you unit test your domain then? You can't really mock entity framework. Should unit tests be setup using an actual database?
@CodeOpinion
@CodeOpinion 2 года назад
Check out this video if you haven't already where I talk about that: ru-vid.com/video/%D0%B2%D0%B8%D0%B4%D0%B5%D0%BE-64ngP-aUYPc.html
@majormartintibor
@majormartintibor 2 года назад
"Repositories should have a get and a save that's it" or something like this. A question: what about List()? I mean without any filtering logic, just a List of all.
@CodeOpinion
@CodeOpinion 2 года назад
Might be some type of GetByIDs() to get more that none that can return a List. Could be required if you were doing some type of batch processing.
@TheHabibass
@TheHabibass 3 года назад
You've mentioned how your repositories only have save and get methods, but where do all the queries go then?
@CodeOpinion
@CodeOpinion 3 года назад
I generally don't use repositories for queries (in CQRS sense). I don't use them as a data access abstraction but as a way to build and save an aggregate on the Command side. Queries generally go directly to the source and get the data how I need it for the given query.
@kiyoshi87
@kiyoshi87 2 года назад
Hi 👋, what if aggregate root require to load the data from persistence before perform and state changes? without event sourcing 😅
@CodeOpinion
@CodeOpinion 2 года назад
Yes, I often use a repository to do the I/O with the DB and build the aggregate. Check out this video: ru-vid.com/video/%D0%B2%D0%B8%D0%B4%D0%B5%D0%BE-01lygxvbao4.html
@martinnicolas1399
@martinnicolas1399 3 года назад
I am using CQRSLite, I store the domain events. When I perform an action on the domain, I need to load the aggregate in memory (CQRSLite replays all the events for the aggregate I am asking it for). BUT I am never persisting the state of my aggregates, they are always replayed, am I doing it wrong?
@CodeOpinion
@CodeOpinion 3 года назад
No. It's typical to replay all events in a stream to get to current state in your write side.
@feliper.alcantara3024
@feliper.alcantara3024 3 года назад
No. But one alternative to this could be Actor model, which you keep the aggregate state in memory.
@misters6451
@misters6451 Год назад
Thank you for the video! But don't you think that with this approach a model becomes anemic? And it's complicated to understand a business logic because it becomes distributed into many classes with behavior.
@CodeOpinion
@CodeOpinion Год назад
Not sure how it's anemic? You're still encapsulating data, it's just the aggregate root isn't an entity.
@rcosta551
@rcosta551 2 года назад
Hi, I find out an interesting useful template to write aggregate information known as Aggregate Canvas. If you have already seen it I have the following question. Which box on Aggregate Canvas should I describe a specification pattern business rules? Also, I would like to know if makes sense to describe a domain service on Bounded Context Canvas?
@CodeOpinion
@CodeOpinion 2 года назад
Referring to this? github.com/ddd-crew/aggregate-design-canvas Business rules would be defined in "Enforced Invariants".
@rcosta551
@rcosta551 2 года назад
@@CodeOpinion But in some cases a specification may not consider business invariants.
@BobLaTige
@BobLaTige 3 года назад
What's the value of generating events if you do not persist them but instead mutate the db ?
@kevinof1978
@kevinof1978 3 года назад
The event recording in this example is used to track changes so that only necessary changes are effected when the aggregate root is later persisted. If you were implementing event sourcing, you would persist the events themselves, because the events are what actually matter in such a system. Derek was talking about systems which persist state, which are more common. He used the events temporarily for change tracking only.
@br3nto
@br3nto 2 года назад
In the second example it seems ShoppingCartItem isn’t actually needed. It’s just the product ids that are really of interest. The ShoppingCartItem is really just bloat in this example
@patryktrochowski9961
@patryktrochowski9961 2 года назад
It would be great if you decided to share your code by a repository. The analyzing of code would be easier.
@CodeOpinion
@CodeOpinion 2 года назад
All the source for any demos is available to channel members or on Patreon ru-vid.com/show-UC3RKA4vunFAfrfxiJhPEplwjoin www.patreon.com/codeopinion
@vineshcool5826
@vineshcool5826 2 года назад
Where do i get the source code for this?
@CodeOpinion
@CodeOpinion 2 года назад
Check out the community tab as there is a few post with links to where all the source code is for members.
@FilipBekic01
@FilipBekic01 3 года назад
Let's say you're using Entity Framework and you make entity as private attribute of root aggregate, as you did. How to resolve project dependency here? Infrastructure project already has Domain as dependency. You can't add Infrastructure to Domain project, it will throw circural dependency error?
@CodeOpinion
@CodeOpinion 3 года назад
You're assuming Clean Architecture, which I don't necessarily prescribe to. Check out this post on Vertical Slices: ru-vid.com/video/%D0%B2%D0%B8%D0%B4%D0%B5%D0%BE-cVVMbuKmNes.html
@megr
@megr 2 года назад
Shouldn't be simpler just to fetch the data and use a mapping Nuget package to return an actual DTO? Seems to me you are creating/mapping a DTO from scratch when there are already packages to do so.
@CodeOpinion
@CodeOpinion 2 года назад
I was illustrating EF which is doing the mapping. If you aren't using that and are using even something else you can use that to do the mapping, sure. Ultimately it's the same issue though is you need something to do change tracking.
@skewty
@skewty 3 года назад
Where is your CodeOpinion public source code repo? hint, hint :) GitLab or GitHub preferred
@CodeOpinion
@CodeOpinion 3 года назад
All the source code in any of the demos is available to Developer level members of my channel. If you're interested, click the join button on my channel for more info.
@jaimezilberberg
@jaimezilberberg 3 года назад
where is the code? I can't find it in the community tab
@CodeOpinion
@CodeOpinion 3 года назад
I'll post tomorrow for the next video with a link where you can access all the source for all the videos! Thanks again for your support. It's appreciated.
@lucasterable
@lucasterable 10 месяцев назад
rehydration
@OllyWood688
@OllyWood688 3 года назад
See that's why I never want to be the smartest dude at work. So much to learn if you have a colleague like this.
@CodeOpinion
@CodeOpinion 3 года назад
We all have something to contribute and can learn from. You just need to share it!
@OllyWood688
@OllyWood688 3 года назад
@@CodeOpinion Well would you be interested in SSE (Server-sent Events) events using only NancyFx, no Websockets, no SignalR, no IIS? Because that's the latest thing I did. Edit: It's also not pollspam until you hear something. It's basically just reaaaally long timeouts to hold a connection open. Edit 2: Okay it's long polling.
@siquod
@siquod 11 месяцев назад
Non-programmers now know how plumbuses are made.
@cya3mdirl158
@cya3mdirl158 3 года назад
Oh no examples in c# :(. Stupid code style. We need more examples in Kotlin, Scala, Groovy, Go, Java
@CodeOpinion
@CodeOpinion 3 года назад
Not sure what the response should be to this? Sorry? The concept is just passing in an data model object to your aggregate (domain). If you're using SQL, generate events (objects), that your repository can use to determine the appropriate SQL for those state changes.
@marna_li
@marna_li 3 года назад
I have been thinking about defining methods directly in entities. It is convenient for encapsulating behaviors. You know where simple behaviors are. Like CartItem.IncrementQuantity)(). Or even methods that check with the DbContext whether, or not, you can add an Item to the Cart. - BTW. CartItem.IncrementQuantity() makes no sense in an event-driven system. It really breaks when you need to inject services in them - like the DbContext. EF Core does inject the DbContext - as of yet only that, not custom services. However, when you create entities yourself (new CartItem(dbContext)), to pass DbContext manually. That is ugly! Mainly because you have to expose your DbContext in order for EF Core dependency injection to work. To make that pretty, you need a factory pattern to hide that code. For all those entities that take DbContext in their constructor, or else the something might break. More complexity in some case. I would still focus on defining Commands, Queries, and Events rather than putting too much logic in my entities.
@CodeOpinion
@CodeOpinion 3 года назад
If you need a dependency for a method, make it a param on the method. Double dispatch isn't evil.
Далее
Decomposing CRUD to a Task Based UI
9:13
Просмотров 18 тыс.
Aggregate Design: Using Invariants as a Guide
8:35
Просмотров 38 тыс.
小丑调戏黑天使的后果#short #angel #clown
00:16
Is an Anemic Domain Model an Anti-Pattern?
9:54
Просмотров 22 тыс.
Are You Accidentally Crippling Your EF Core Queries?
17:18
Domain Modeling Gone Wrong - Part 1
8:21
Просмотров 27 тыс.