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!
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.
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.
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.
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.
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.
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.
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.
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.
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?
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.
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.
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?
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)?
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).
@@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)
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.
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.
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?
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?
@@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.
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?
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?
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.
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?
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?
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?
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?
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?
@@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.
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.
@@jamesalcaraz8729 you can use Fluent API instead of annotations. so put your data models under Domain and write the EntityConfiguration in Infrastructure.
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.
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?
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)
Writing tests to me doesn't change much. For the last version using events, check out this video as an example: ruclips.net/video/rGlNhYOqKZk/видео.html
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.
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.
@@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 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.
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?
"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.
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?
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.
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?
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.
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 :)
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?
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.
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.
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.
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 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
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.
@@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?
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.
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..
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... :(
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
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!
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.
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?
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.
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.
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 ruclips.net/video/64ngP-aUYPc/видео.html
@@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 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?
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 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.
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.
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!
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?
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.
@@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?
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.
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
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?
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?
All the source for any demos is available to channel members or on Patreon ruclips.net/channel/UC3RKA4vunFAfrfxiJhPEplwjoin www.patreon.com/codeopinion
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?
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.
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.
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?
You're assuming Clean Architecture, which I don't necessarily prescribe to. Check out this post on Vertical Slices: ruclips.net/video/cVVMbuKmNes/видео.html
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 ?
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?
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.
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.
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.
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.
@@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.
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.
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.
I like how the Save method was just processing events and actually will work will with EF change tracking as well
The best youtube channel ever, thanks Derek!
Thanks for watching!
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!
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.
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.
@@CodeOpinion @MilanJovanovicTech I'm wondering, is it ok to have domain layer to depends on something, particularly to infrastructure?
I really appreciate your explanation of an aggregate.
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.
Great video. Perhaps exposing the "Items" property as IReadOnlyCollection to prevent modifications to the Items list outside the aggregate root.
As a Java - Springboot guy, I find this very useful. Nice work!
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.
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.
Same here. One of the best tech decisions I've ever made.
Same boat.
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.
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.
@@adrielairaldo Then it's better to have data duplication with message queues. Although that kind of sucks too.
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.
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.
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?
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.
@@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
@@CodeOpinion Yes pretty please, do that video.🙏
DbContext implements the Unit Of Work pattern. No reason to implement a UoW on top of another...
Pretty cool example, simple to follow, great for us new to the architecture.
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.
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?
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
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)?
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).
@@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)
I was looking for that, thanks.
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.
Thanks! Appreciate the sub.
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.
I really love your code opinion :)
Absolutely agree with you!
Glad to hear it!
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?
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?
@@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.
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?
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?
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.
@@CodeOpinion Thanks for your reply. When you say source, what do you mean? Do you mean the DbContext? Just want to be sure. Thanks.
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?
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.
Hey, nice video, I couldn't find a link to source code used for this example, is it available somewhere?
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?
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?
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?
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?
@@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.
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.
I think I need to make a video about this to explain more. Stay tuned for that.
@@jamesalcaraz8729 you can use Fluent API instead of annotations. so put your data models under Domain and write the EntityConfiguration in Infrastructure.
Really great videos, keep it up.
Thanks, will do!
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.
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?
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)
Writing tests to me doesn't change much. For the last version using events, check out this video as an example: ruclips.net/video/rGlNhYOqKZk/видео.html
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.
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.
@@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?
@@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.
@@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.
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?
Sure, however you want to do change tracking ultimately is what it comes down to.
"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.
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.
What about delete/add? Should that go through an aggregate domain model or do you use DbContext right away?
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?
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.
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?
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.
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.
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 :)
@@CodeOpinion it does generate query including the unchanged column in the latest EF Core 6 (using SQL server as a provider)
@@gyanookharel7440 did you by any chance disable tracking for that query then call the update method manually ?
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?
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.
@@CodeOpinion I got it! Thank you!
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.
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.
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.
Not sure what you mean by the repository needs to retrieve data in a perfect way for domain events?
@@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
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.
@@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?
@@andriyroman5422 I also don't understand what you're saying :D
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.
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..
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... :(
How do you unit test your domain then? You can't really mock entity framework. Should unit tests be setup using an actual database?
Check out this video if you haven't already where I talk about that: ruclips.net/video/64ngP-aUYPc/видео.html
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
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!
You've mentioned how your repositories only have save and get methods, but where do all the queries go then?
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.
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?
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.
Where are you going to keep entity framework entities in infra layer ,not domain layer,are not we?
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.
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 ruclips.net/video/64ngP-aUYPc/видео.html
@@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.
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.
@@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?
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?
Second example being the domain model example where the data model is being loaded into it? Not entirely sure what you're referring to.
@@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?
@@br3nto The same as the first. The Shopping cart data model which is attached/tracked to EF is as passed into the ShoppingCartDomain
@@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.
Hi 👋, what if aggregate root require to load the data from persistence before perform and state changes? without event sourcing 😅
Yes, I often use a repository to do the I/O with the DB and build the aggregate. Check out this video: ruclips.net/video/01lygxvbao4/видео.html
Hi @CodeOpinion,
Why do you separate Domain class from ef model? Is it not extra work?
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.
@@CodeOpinion Yes, of course, In some cases, it will be helpful. Thanks
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!
Yup, I'll re-comment with the link when it's up
Members get the slides too :)
The only thing to take in mind is. Azure table storage, supports transaction only within one partition key.
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?
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.
@@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?
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.
Sorry, not sure I'm following when you say include another domain repository? Yes, EF implements repository, unit of work, and specification.
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
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?
Don't. Put your data models with your domain model.
@@CodeOpinion Okay, but then instrastructure layer still needs to reference domain, instead of just application layer. I don't understand that part.
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?
Where do i get the source code for this?
Check out the community tab as there is a few post with links to where all the source code is for members.
It would be great if you decided to share your code by a repository. The analyzing of code would be easier.
All the source for any demos is available to channel members or on Patreon
ruclips.net/channel/UC3RKA4vunFAfrfxiJhPEplwjoin
www.patreon.com/codeopinion
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?
Referring to this? github.com/ddd-crew/aggregate-design-canvas
Business rules would be defined in "Enforced Invariants".
@@CodeOpinion But in some cases a specification may not consider business invariants.
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.
Not sure how it's anemic? You're still encapsulating data, it's just the aggregate root isn't an entity.
What's the value of generating events if you do not persist them but instead mutate the db ?
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.
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?
You're assuming Clean Architecture, which I don't necessarily prescribe to. Check out this post on Vertical Slices: ruclips.net/video/cVVMbuKmNes/видео.html
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 ?
Check out the video I posted about design aggregates around invariants. That might help. ruclips.net/video/64ngP-aUYPc/видео.html
@@CodeOpinion Thank you !
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?
No. It's typical to replay all events in a stream to get to current state in your write side.
No. But one alternative to this could be Actor model, which you keep the aggregate state in memory.
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.
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.
rehydration
Where is your CodeOpinion public source code repo? hint, hint :) GitLab or GitHub preferred
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.
Separate behavior from data... What does that remind me of...
where is the code? I can't find it in the community tab
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.
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.
We all have something to contribute and can learn from. You just need to share it!
@@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.
Non-programmers now know how plumbuses are made.
Oh no examples in c# :(. Stupid code style. We need more examples in Kotlin, Scala, Groovy, Go, Java
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.
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.
If you need a dependency for a method, make it a param on the method. Double dispatch isn't evil.