Adding a repository layer on top of EF is just wrapping one repository pattern with another. The DbSets in your context are already your repositories, and the DbContext itself implements the unit of work pattern. It's worth bearing in mind that patterns are often derived from other languages (especially Java) that lack nice things like extension methods in C# so there's sometimes better ways to approach things. For example you can extend the DbSet collections in your EF context with extension methods to add methods like GetProductById to DbSet. You can even add generic GetById methods to all your DbSets using a single extension method. It's much cleaner this way, with less code and less complexity.
I'm focusing on my Domain design, and dictating what the Application layer can do with those repositories. You're missing the point, because "EF is a repository already" doesn't matter. You can use just EF, and you'll be fine. But that has other implications for your application.
This is a very unintuitive solution. No one expects to use the logic coded only in extension methods + by adding extension methods to DbSet which is a strictly EfCore class you tie your project to use EntityFramework only and you are not able to switch it to different project/database connector.
Seriously, this is like repeating the same argument over and over. Specific repository or generic are not silver bullets. Also, there will be projects you will come across multiple database providers, and the generic repository might be useful. Also, combining it with the specification pattern helps reduce the number of overloads for read commands.
How would this work if you wanted to mock out the db for testing and use in memory data instead of data in a db? In this case that you mention, you are stuck with using EF and a db
i love that too, i am using dapper and i dont know how call the base repository method from my service, calling the specific repository interface with DI :S
This is an approach I used in a project where you had to connect to multiple providers via API and keep their information in our db, this helped a lot to reduce the code generated via the integration of every provider, and we could have a lot of providers almost seamlessly
@@MilanJovanovicTech it was a tourism related company so they were APIs like booking, Expedia... We used that pattern to keep all the different information for all these providers in different tables
I like to use generic repositories these days, since many database providers - relational or not - support queryable linq and it's relatively easy to implement an in-memory cache or change tracking system for any of them that don't support EFCore. When I need a custom operation on the repository, lets say it due to a need of a very optimized query or bulk update or insert, then I create a specific method in the domain repository interface. This has worked very well for many projects I've worked on.
When using generic repository pattern with EF I always liked to make the methods simple and return an Iqueryable so the inheriting class can modify the "Base query" and add to it. E. G the repository class has a getById but the inheriting class can call this, get the Iqueryable and add a where clause to it to filter further more but only actually executing once.
Thank you for your answers. I will approach things like this in my future applications I don't know is it true or not. I will use repository pattern in basic crud operations becauase it is good especially when you want implement something like soft delete mechanism. But in complex queries no matter I use cqrs or not I will do in bussiness/application layer because it really gives complexity. I really see a lot solution people does not use select query and directly map into objects. What is the reason to get all the data from database and map to them. Because if you get 8 columns of data maybe you need 4 columns of it..Map after get all the data. Select after ToListAsync.All columns queired allready!. This is performance issue.If it is wrong to use dtos in repositories object or dynmaic i should do this operation in bussiness layer. So I should abstract away dbcontext because my application shouldn't reference persistence layer.I am really tired to search how to solve this. Thanks for the answer again.
I'm learning a lot on your videos. 😁 I'm just wondering if, is it better to have a method GetAsync with a parameter of an expression (Expression predicate = null) rather than adding a method GetByIdAsync ?
@@MilanJovanovicTech when reading for modifying, yes! I've used another set of dbcontexts with plain hasmany-relations for readmodels. Not sure if it's a good idea in the long run, but it has been ok so far.
But you don't solve the problem with interface that your repository don't need - CustomerRepository needs only generic Add , but get also Remove and Update .
Not quite. Because the interface isn’t exposing the methods, they cannot be accessed outside of the Infrastructure layer. The domain & application layer will only use the interfaced methods :)
You can argue that you most likely always need CRUD actions on an entity. But I do get our point. There are definitely scenarios where you don't need the full CRUD actions.
Really good and concise video explaining the pattern. I have one question that I couldn't find an answer on. What if we are working with a DbContextFactory? How would the Repository and UnitOfWork class use the context in this case?
@@MilanJovanovicTech So we wouldn't inject the repository with DI but instantiate it in the calling method with a created context? Or is there another way to make the repository use the created context, preferably with DI? Sry for the stupid question 😅 Because what I think the problem is, when having DbContextFactory in repository class, it always creates a new context for each repository method, but I would like the repository and the unit of work to use the same context. Maybe I'm just missing something
It is interesting that you decided to define the repository interfaces within the domain project. I myself usually define them within the application layer, because it is actually the application that determines what exact data it needs, based on what the application actually does. The domain layer should not be bothered with that. Of course it does still own its own entities, value objects and contains all related domain logic. One down side of my way however is that a lot of these repository interfaces end up in the Common area, since their usage often spans across multiple application microservices... And then also your reasoning does make sense and got me thinking about it once again. I've yet to decide if we will be switching back to defining 'em in the domain layer after all :)
Maybe we can look at it as "domain repositories" and "application repositories"? The naming might be a bit strange, but I think it describe what you/me are doing
Great video Milan. Would love to see how something like this would work with the MongoDB driver. First thought is that each collection is it's own property in the MongoDB context and can't just use the generic Set property. As far as I'm aware?
@@MilanJovanovicTech Yes, it avoids having to write repeated code in the repositories and it allows you only to write specific queries only for certain entities
Yes, that's one of the drawback of ORMs. If you want to load an entity, it'll have all the columns. You could optimize it by manually fetching specific columns, but most of the times you wouldn't do this if your intent is to write back to the DB. If you are using an ORM for a query (read) than you will definitely only want to fetch the columns you need.
This is not an ORM drawback! In ef core you can do it how you like it! Either you project your entities onto a dto, and EF CORE will only select the columns needed. This is good for readonly operations! In this case however, when trying to do DDD, you want your aggregates to be fully loaded into memory, because the aggregates define your consistency boundary. The aggregate can now use all the information in the aggregate to validate the operations your doing on it! If this means pulling too many unwanted columns from the database, maybe your aggregate is to big? Maybe it holds many unrelated fields, that don't have to be inside the same consistency boundary?
Thanks for this helpful video Milan! Wouldn't it be great to use this generic repository pattern with CQRS and only the Command uses it and the Query part still uses EF directly? Should be a good content for another video?
Nice video, Milan. But here, inspite of need an add method for an entity, the entity has now four or more method by inheriting the generic repository. 1. Then What is purpose of using interface? 2. Why the repository expose the methods that doesn't need?
Год назад
Remember that you always use the repo interfaces defined at the domain layer. You don't have access to the members of the implementation so you won't see the generic repo methods.
You said that the domain layer dictates how the repository interfaces should be implemented but isn't it vice versa? it looks like knowing that the reads and writes are separated and ef is an orm forces you to create such interfaces where the reads are async methods and writes are not.
Repository doesn't do writes, just adds stuff to the repo. UoW takes care of write. I don't think async (Task) in domain is bad, anyhow. The repository dictates the usage (not the implementation).
HI @MilanJovanovicTech, I really enjoy watching your tutorials. Please do you have any video on unit testing ef core async methods like AnyAsync, FirstOrDefaultAsync, e.t.c as well as AsNoTracking method? Thanks
I asked this to GPT and this is the answer. But it is still not a good answer for me. When using the Entity Framework (EF) in .NET, the DbContext class already provides a SaveChanges() method, which might make it seem redundant to wrap this with a UnitOfWork. However, implementing the UnitOfWork pattern can still be beneficial, even if all you're doing is calling SaveChanges(). Here's why: Abstraction: UnitOfWork abstracts the underlying data access logic from the rest of the application. By doing so, it makes your application less dependent on a particular ORM or database technology. If you ever need to change your data access logic or even switch to another ORM, you'll have a single point of change. Testability: With the UnitOfWork pattern, you can mock your unit of work in unit tests. This makes testing easier, especially if you want to test services that have database interactions without actually hitting the database. Multiple Data Sources: In more complex applications, you might be dealing with multiple databases or data sources. A UnitOfWork can help manage transactions across these different sources. Decoupling: It allows services to be decoupled from the specific persistence mechanism being used, making the architecture cleaner and more maintainable. Flexibility: In the future, if you need to add more functionality around the commit (like logging, event raising, etc.), you can do it in the UnitOfWork without altering your service or repository classes. Transaction Management: The pattern can simplify transaction management, especially if there's a need to handle custom business transactions that span multiple operations or repositories. Clarity: By using the UnitOfWork pattern, you're signaling to other developers that you're aggregating operations for eventual transactional persistence. This can help in understanding the application's flow and logic.
@MilanJovanovicTech Isn't that the main responsibility of a repository? Your get methods already access the DB. Excluding SaveChanges from the repository and renaming it to UnitOfWork outsources transaction handling to application logic.
@@Greenthum6I guess because you don't really know how many repositories you wanna touch before committing the transaction.Even though most often you'd only want a single aggregate to participate in your transaction.
Hi Milan! How do you deal with models in domain driven design that don't have behavior? In my project great majority of domain models have rich business logic (like 95%), but sometimes I encounter something that should just be a dumb data container, and it doesn't make sense to make it an aggregate root. For example when I create some Order aggregate, the client needs to pass some OrderOptions, like product's color, product's material, etc.. These cannot be a value object, because these order options have concrete price assigned to them, and should be seeded into the database (probably not gonna change often). So the only option I see is that the order option is an aggregate root, and then in Order.Create() factory method I can pass List. The thing I don't like about this approach is that OrderOption doesn't really have any behavior, other than maybe changing a price, that could raise some domain event. But still it doesn't seem like a good fit for an aggregate. Do you think that I should create some directory in domain layer for something like DataModels and throw anemic models there? I'm really curious how you'd handle this.
Just because it's anemic doesn't mean it's wrong. Some parts of the Domain simply won't have any behavior. Take a look at this for static data: ruclips.net/video/v6cYTcEfZ8A/видео.html
You make the generic repository class in which all methods depend on the type of class. While I think there should be generic methods which are independent and called for any entity type.
@@MilanJovanovicTech don't make custom repositories like OrdersRepository or SalesRepository etc. Just make a generic Repository by using specification design pattern.
Hello if I want to implement select can I use dto in repository is it good practice what is your suggestion? Some people says repository should return domaim entities repository should not use dtos
1) can we write : throw NotImlementedException in child repository classes, to suppress not required methds of base Generic repo class? 2) If not then, this Generic repository approach is not usefull where only writing method (POST) is there. Forex: in case of some business transations, having only adding new records into db table is enough. Plz give your comments
@@ravindranaths513 Throwing NotImplementedExceptions is only for methods that are yet to be implemented (and _will_ be implemented). They should never be thrown purposely otherwise. In this case you'll simply get the methods for free within other repositories too, even if you don't (currently) need them 😉
What do u think about second optional parameter in GetByIdAsnc where you have array of expresionss. If this array is not null we just applay all includes expresionss.
Great video, EF is a repository 😂 I think they missed the point. Would love to see a video on GUID vs X for id's that one also gets a lot of debate. I notice you use GUIDs, do you do this on large systems?
There are sequential GUIDs and ULIDs without the drawbacks. But GUIDs are mostly fine (albeit random). And in a distributed system you have to use something like that.
Regarding the Include: personally I think this is a requirement only the consumer of the repository knows about. And I think the repository shouldn't specify it because its related to the domain logic. One consumer of a repository method might need 2 types of .Include() while another might need 3 others or another .ThenInclude(). You then have two choices: either let all consumers of a method fetch the total set of Include() flavours or you let the consumer specify the include as a functional parameter to the repository method.
@@MilanJovanovicTech You could use the specification pattern but that doesn't necessarily address the issue. Depends on the implementation.🙂 In a real world example a repository method can and should be able to be used by multiple consumers. But rarely they have the exact same needs when it comes to includes.
@@Benke01 Then just let the repository offer multiple variants of the method, either by adding an additional parameter or just create a few extra methods that include specific things.
I see in your examples unit of work in command. But if need call multiply commands and after that call unit of work. It is need to do in controller if we speak about mediator ?
@@MilanJovanovicTech But then if i want to create specific command need to create a lot of diff commands. But better in conroller call multiply small comands and after that call commit
First of all, great video! I really enjoy your work so keep it up! I am currently working on a clean architecture solution and I'm are quite new in the concept and noticed that you refer the domain layer in infrastructure. Is it alright that infrastructure can reference to classes in the domain layer or is it only application who have access to this layer? I mean based on the clean architecture diagram, the domain layer is the inner most layer where the application layer is the adjacent layer to domain while presentation and infrastructure are the outer layers. So im just a bit confused on how outer layers can reference each inner layers without breaking best practices? 😅
What happens when mediator fails to handle your events? They just dissappear, and the initial transaction (your savechanges) is completed! This is not good is it? The change you made in the domain is probably not idempotent, so you can't just run it again and hope the events succeed next time. Shouldn't you save the events along with the entity in a transactional outbox!?
@@MilanJovanovicTech This? ruclips.net/video/BimfDeDV4yU/видео.html Because it only briefly touches the transactional outbox at the end. It seems like you have solution for this without transactional outbox hidden somewhere, but I can't find it, nor understand how it would work. :)
Oh. This one? It is just as problematic as it was before introducing events, only now the chain of events can be basically infinite and crash anytime? Transactional outbox is the only way, as I see it. ruclips.net/video/AHzWJ_SMqLo/видео.html
@MilanJovanovicTech I thought we should only create repositories for aggregate roots in DDD pattern? And 2nd question, if I want to implement a custom query for an entity which is not an aggregate root, where should I put that
@@Code_Bits can you explain why ? I have UnitOfWork with Generic Repository and I thought with Stored Procedure you can avoid sql injection?!? So what do you suggest?
@@MilanJovanovicTech No, I'm talking context. You explain why you use it. I am asking why not use it in a specific way, as all you're doing is recreating functionality that is already available.
Hello. How should I implememt select with this pattern. I implemented one but it is complex. Should i implement another overload for select functionality thanks for answer.
@@MilanJovanovicTech i mean doest have to be the same dbcontext, used in the repository class or two scoped instances are ok, one to do the databas access one will do the save changes. What I had before a method within the abstract reposotory class, this method will do save changes.
Should Domain project be allowed to reference in Persistence project? As per clean architecture diagram, domain should be referenced in Application project only?
@@MilanJovanovicTech ok, but if one may want to keep both layers independent of each other then in which project domain and DB entities should be mapped to each other and what would be appropriate way of mapping them?
awesome! it's quite similar to the approach we've used at my previous work. what do you think of accessing generic repository via a factory or builder of some sort? would it be bad to expose generic repo building based on specified entity type, even if the entity doesn't have an concrete repo?
You can directly use Entities without an concrete repository for them. So you'll only be able to use the Add(), Update() etc... methods for that specific entity. If you implement the repositories the way Milan implemented them you could also make these methods virtual and override them in your specific repositories if needed. At work we also used this implementation of the generic repository pattern and it's a very good way imho. We also used a IUnitOfWorkFactory to create the Unit of Work and retrieving from the dependency injection container the repositories we needed.
@@MilanJovanovicTechI think there were some ideas with exposing Set() DbContext method (where TEntity is IEntity, of course) and some scoped caching of the built repositories in a concurrent collection (if the repo is needed again in the same request)
@@MilanJovanovicTech I don't have an example unfortunately, however the Factory created an UnitOfWork instance with the right DbContext. This UnitOfWork instance was used to get repositories (like the Product repository) which only accepted domain entities and automatically mapped them to the DbEntity (in case of an Add()). In case of an any other operation like Update() etc we also passed in a domain entity which would update the DbEntity. The repository took care of mapping them and you only needed to have the mapping configured and the repositories registered in your DI container. This worked very well in our case since as you can guess you only needed to inject the factory within your classes and not the UnitOfWork and repositories.
@@MilanJovanovicTech Its nice to put an interface around things for many reasons! The repository is not reinvented by doing this. Its just getting a nicer packaging, communicating through specific interfaces what operations are allowed. Also, you gain mockability.
Want to master Clean Architecture? Go here: bit.ly/3PupkOJ
Want to unlock Modular Monoliths? Go here: bit.ly/3SXlzSt
sir, you just got my voice ! 🫡
Adding a repository layer on top of EF is just wrapping one repository pattern with another. The DbSets in your context are already your repositories, and the DbContext itself implements the unit of work pattern.
It's worth bearing in mind that patterns are often derived from other languages (especially Java) that lack nice things like extension methods in C# so there's sometimes better ways to approach things. For example you can extend the DbSet collections in your EF context with extension methods to add methods like GetProductById to DbSet. You can even add generic GetById methods to all your DbSets using a single extension method. It's much cleaner this way, with less code and less complexity.
I'm focusing on my Domain design, and dictating what the Application layer can do with those repositories. You're missing the point, because "EF is a repository already" doesn't matter. You can use just EF, and you'll be fine. But that has other implications for your application.
i had thousand problems with tis method, dont use in large scale projects
This is a very unintuitive solution. No one expects to use the logic coded only in extension methods + by adding extension methods to DbSet which is a strictly EfCore class you tie your project to use EntityFramework only and you are not able to switch it to different project/database connector.
Seriously, this is like repeating the same argument over and over.
Specific repository or generic are not silver bullets.
Also, there will be projects you will come across multiple database providers, and the generic repository might be useful.
Also, combining it with the specification pattern helps reduce the number of overloads for read commands.
How would this work if you wanted to mock out the db for testing and use in memory data instead of data in a db? In this case that you mention, you are stuck with using EF and a db
Nice video, i’d love to see a version of this with Dapper instead of efCore.
Dapper one would be much more verbose on the implementation side - and we don't have a UoW 😁
Right, i use dapper in my project and have some repetition with the scenarios you solve in the video.
Dapper benefits from repository pattern much more than EF
i love that too, i am using dapper and i dont know how call the base repository method from my service, calling the specific repository interface with DI :S
Hey there. Long time sub from Cameroon. Keep up with videos, i learn a lot from them.
Thanks a lot, glad you're getting value from the videos :)
This is an approach I used in a project where you had to connect to multiple providers via API and keep their information in our db, this helped a lot to reduce the code generated via the integration of every provider, and we could have a lot of providers almost seamlessly
That's awesome. Which providers were you working with?
@@MilanJovanovicTech it was a tourism related company so they were APIs like booking, Expedia... We used that pattern to keep all the different information for all these providers in different tables
I like to use generic repositories these days, since many database providers - relational or not - support queryable linq and it's relatively easy to implement an in-memory cache or change tracking system for any of them that don't support EFCore. When I need a custom operation on the repository, lets say it due to a need of a very optimized query or bulk update or insert, then I create a specific method in the domain repository interface. This has worked very well for many projects I've worked on.
Very nice approach!
When using generic repository pattern with EF I always liked to make the methods simple and return an Iqueryable so the inheriting class can modify the "Base query" and add to it. E. G the repository class has a getById but the inheriting class can call this, get the Iqueryable and add a where clause to it to filter further more but only actually executing once.
At that point - why not use EF Core directly?
Valious tips Milan!! Depending of project, can be useful
Everything has a use case
Patreon email coming in clutch to quickly absorb Milan's new content
Haha! You're fast 😁
Thank you for your answers. I will approach things like this in my future applications I don't know is it true or not. I will use repository pattern in basic crud operations becauase it is good especially when you want implement something like soft delete mechanism. But in complex queries no matter I use cqrs or not I will do in bussiness/application layer because it really gives complexity. I really see a lot solution people does not use select query and directly map into objects. What is the reason to get all the data from database and map to them. Because if you get 8 columns of data maybe you need 4 columns of it..Map after get all the data. Select after ToListAsync.All columns queired allready!. This is performance issue.If it is wrong to use dtos in repositories object or dynmaic i should do this operation in bussiness layer. So I should abstract away dbcontext because my application shouldn't reference persistence layer.I am really tired to search how to solve this. Thanks for the answer again.
Nice 👌
Thanks for sharing your valuable knowledge.
My pleasure
I'm learning a lot on your videos. 😁
I'm just wondering if, is it better to have a method GetAsync with a parameter of an expression (Expression predicate = null) rather than adding a method GetByIdAsync ?
If you want that flexibility then yeah, go for it
The order and orderlines could be defined using the ownsmany-relationship, and efcore will handle the include for you, so you can skip the override.
Do you always want to include them, though?
@@MilanJovanovicTech when reading for modifying, yes! I've used another set of dbcontexts with plain hasmany-relations for readmodels. Not sure if it's a good idea in the long run, but it has been ok so far.
But you don't solve the problem with interface that your repository don't need - CustomerRepository needs only generic Add , but get also Remove and Update .
Not quite. Because the interface isn’t exposing the methods, they cannot be accessed outside of the Infrastructure layer. The domain & application layer will only use the interfaced methods :)
You can argue that you most likely always need CRUD actions on an entity. But I do get our point. There are definitely scenarios where you don't need the full CRUD actions.
@@TheFeljoy I mentioned only interface methods . Dont get your point.
@@Forshen So Milan said that he does not like it because all repo's get access to full crud , but we can dont need it . And than make same stuff.
I'm not worried about having these methods in the implementation, since they aren't exposed to the consumer
Really good and concise video explaining the pattern. I have one question that I couldn't find an answer on. What if we are working with a DbContextFactory? How would the Repository and UnitOfWork class use the context in this case?
Configure them to resolve the DbContext form the factory? 🤔
@@MilanJovanovicTech So we wouldn't inject the repository with DI but instantiate it in the calling method with a created context? Or is there another way to make the repository use the created context, preferably with DI? Sry for the stupid question 😅 Because what I think the problem is, when having DbContextFactory in repository class, it always creates a new context for each repository method, but I would like the repository and the unit of work to use the same context. Maybe I'm just missing something
What exactly are the OrderId, ProductId classes?
Strongly Typed IDs: ruclips.net/video/LdrMdIabE1o/видео.html
Great video. if the app logic is complex. we better make a abstract class between actual service class and their interfaces. it is more flexible.
Thanks!
It is interesting that you decided to define the repository interfaces within the domain project. I myself usually define them within the application layer, because it is actually the application that determines what exact data it needs, based on what the application actually does. The domain layer should not be bothered with that. Of course it does still own its own entities, value objects and contains all related domain logic.
One down side of my way however is that a lot of these repository interfaces end up in the Common area, since their usage often spans across multiple application microservices... And then also your reasoning does make sense and got me thinking about it once again.
I've yet to decide if we will be switching back to defining 'em in the domain layer after all :)
Maybe we can look at it as "domain repositories" and "application repositories"? The naming might be a bit strange, but I think it describe what you/me are doing
@@MilanJovanovicTech Well that does indicate the difference.
thought the same thing. interesting approach but I would not put them in the Domain, good to hear I wasn't the only one thinking of that.
Great video Milan. Would love to see how something like this would work with the MongoDB driver. First thought is that each collection is it's own property in the MongoDB context and can't just use the generic Set property. As far as I'm aware?
Will check to see how this works with MongoDb. Could possibly require some re-design. 🤔
Awesome, love your explanation. Earned a subscriber. 💪
Thanks a lot! Hope you found the other videos just as valuable 😊
I really like this approach when using ef core with the repository pattern
Did you use it before?
@@MilanJovanovicTech Yes, it avoids having to write repeated code in the repositories and it allows you only to write specific queries only for certain entities
I'm primarily a DB developer so forgive my ignorance but does entity retrieve full objects from the DB even if you only use a few columns?
Yes, that's one of the drawback of ORMs. If you want to load an entity, it'll have all the columns. You could optimize it by manually fetching specific columns, but most of the times you wouldn't do this if your intent is to write back to the DB. If you are using an ORM for a query (read) than you will definitely only want to fetch the columns you need.
This is not an ORM drawback! In ef core you can do it how you like it! Either you project your entities onto a dto, and EF CORE will only select the columns needed. This is good for readonly operations! In this case however, when trying to do DDD, you want your aggregates to be fully loaded into memory, because the aggregates define your consistency boundary. The aggregate can now use all the information in the aggregate to validate the operations your doing on it! If this means pulling too many unwanted columns from the database, maybe your aggregate is to big? Maybe it holds many unrelated fields, that don't have to be inside the same consistency boundary?
Great video! Thank Milan.
Sure thing :)
Thanks for this helpful video Milan! Wouldn't it be great to use this generic repository pattern with CQRS and only the Command uses it and the Query part still uses EF directly? Should be a good content for another video?
It's something I've done in future videos, more or less
Isn't Moq caught secretly gathering email addresses?
Yes, in some versions. The versions before that (and after) are fine
It also wasn't really secret, they weren't caught, they announced it
holy this is clean af
Overengineering at its finest 😁🔥
Nice video, Milan.
But here, inspite of need an add method for an entity, the entity has now four or more method by inheriting the generic repository.
1. Then What is purpose of using interface?
2. Why the repository expose the methods that doesn't need?
Remember that you always use the repo interfaces defined at the domain layer. You don't have access to the members of the implementation so you won't see the generic repo methods.
1 - I explained that in the video, in-depth with my reasoning
2 - The repository implementation is internal - you're working with the interface
You said that the domain layer dictates how the repository interfaces should be implemented but isn't it vice versa? it looks like knowing that the reads and writes are separated and ef is an orm forces you to create such interfaces where the reads are async methods and writes are not.
Repository doesn't do writes, just adds stuff to the repo. UoW takes care of write. I don't think async (Task) in domain is bad, anyhow.
The repository dictates the usage (not the implementation).
HI @MilanJovanovicTech, I really enjoy watching your tutorials. Please do you have any video on unit testing ef core async methods like AnyAsync, FirstOrDefaultAsync, e.t.c as well as AsNoTracking method?
Thanks
Maybe this: ruclips.net/video/tj5ZCtvgXKY/видео.html
What are the domain events for in the Persistence layer? Shouldn't they be raised from the Domain/Application layer?
How do you "raise" a domain event?
why would you implement UnitOfWork just to call SaveChanges?
I asked this to GPT and this is the answer. But it is still not a good answer for me.
When using the Entity Framework (EF) in .NET, the DbContext class already provides a SaveChanges() method, which might make it seem redundant to wrap this with a UnitOfWork. However, implementing the UnitOfWork pattern can still be beneficial, even if all you're doing is calling SaveChanges(). Here's why:
Abstraction: UnitOfWork abstracts the underlying data access logic from the rest of the application. By doing so, it makes your application less dependent on a particular ORM or database technology. If you ever need to change your data access logic or even switch to another ORM, you'll have a single point of change.
Testability: With the UnitOfWork pattern, you can mock your unit of work in unit tests. This makes testing easier, especially if you want to test services that have database interactions without actually hitting the database.
Multiple Data Sources: In more complex applications, you might be dealing with multiple databases or data sources. A UnitOfWork can help manage transactions across these different sources.
Decoupling: It allows services to be decoupled from the specific persistence mechanism being used, making the architecture cleaner and more maintainable.
Flexibility: In the future, if you need to add more functionality around the commit (like logging, event raising, etc.), you can do it in the UnitOfWork without altering your service or repository classes.
Transaction Management: The pattern can simplify transaction management, especially if there's a need to handle custom business transactions that span multiple operations or repositories.
Clarity: By using the UnitOfWork pattern, you're signaling to other developers that you're aggregating operations for eventual transactional persistence. This can help in understanding the application's flow and logic.
Would you make each repository method also go to the DB?
@MilanJovanovicTech Isn't that the main responsibility of a repository? Your get methods already access the DB. Excluding SaveChanges from the repository and renaming it to UnitOfWork outsources transaction handling to application logic.
@@MilanJovanovicTech i feel you, i've never thought uow like that, thanks you :) love your content :)
@@Greenthum6I guess because you don't really know how many repositories you wanna touch before committing the transaction.Even though most often you'd only want a single aggregate to participate in your transaction.
Hi Milan! How do you deal with models in domain driven design that don't have behavior? In my project great majority of domain models have rich business logic (like 95%), but sometimes I encounter something that should just be a dumb data container, and it doesn't make sense to make it an aggregate root. For example when I create some Order aggregate, the client needs to pass some OrderOptions, like product's color, product's material, etc.. These cannot be a value object, because these order options have concrete price assigned to them, and should be seeded into the database (probably not gonna change often). So the only option I see is that the order option is an aggregate root, and then in Order.Create() factory method I can pass List. The thing I don't like about this approach is that OrderOption doesn't really have any behavior, other than maybe changing a price, that could raise some domain event. But still it doesn't seem like a good fit for an aggregate. Do you think that I should create some directory in domain layer for something like DataModels and throw anemic models there? I'm really curious how you'd handle this.
Just because it's anemic doesn't mean it's wrong. Some parts of the Domain simply won't have any behavior. Take a look at this for static data: ruclips.net/video/v6cYTcEfZ8A/видео.html
You make the generic repository class in which all methods depend on the type of class.
While I think there should be generic methods which are independent and called for any entity type.
And how would you do that?
@@MilanJovanovicTech don't make custom repositories like OrdersRepository or SalesRepository etc. Just make a generic Repository by using specification design pattern.
Hello if I want to implement select can I use dto in repository is it good practice what is your suggestion? Some people says repository should return domaim entities repository should not use dtos
I'm on the same side of the fence: repositories should only work with domain entities
I'm looking forward to seeing a video explaining about upgrading with multilevel. Is there a way to do this dynamically?
Multilevel what?
@@MilanJovanovicTech Multi children entities
What if any repository class doen't want the functionality (method), which is defined in base repository class?
Well, nothing really. You can't remove it from the base class.
1) can we write : throw NotImlementedException in child repository classes, to suppress not required methds of base Generic repo class?
2) If not then, this Generic repository approach is not usefull where only writing method (POST) is there. Forex: in case of some business transations, having only adding new records into db table is enough.
Plz give your comments
@@ravindranaths513 Throwing NotImplementedExceptions is only for methods that are yet to be implemented (and _will_ be implemented). They should never be thrown purposely otherwise. In this case you'll simply get the methods for free within other repositories too, even if you don't (currently) need them 😉
Very nice and usefull video.
What theme are you using for visual studio?
R#
Hi, what should we do when we find ourselves with a service like UserService that injects, for example, 10 repositories? How can we make it cleaner?
Break it up into use cases? An idea
Thanks for the video and the explanations. Is it possible to have access to the code please?
In the description
What do u think about second optional parameter in GetByIdAsnc where you have array of expresionss. If this array is not null we just applay all includes expresionss.
I'd be careful with expanding the interface like that, but it should be fine
How would you handle tables that don't have Id? Or use composite keys?
Will they need repositories at all?
Composite keys are a bit complex, so probably a slightly different solution
Great video, EF is a repository 😂 I think they missed the point. Would love to see a video on GUID vs X for id's that one also gets a lot of debate. I notice you use GUIDs, do you do this on large systems?
There are sequential GUIDs and ULIDs without the drawbacks. But GUIDs are mostly fine (albeit random). And in a distributed system you have to use something like that.
Where is the link of the code repo?
Patreon
Regarding the Include: personally I think this is a requirement only the consumer of the repository knows about. And I think the repository shouldn't specify it because its related to the domain logic. One consumer of a repository method might need 2 types of .Include() while another might need 3 others or another .ThenInclude(). You then have two choices: either let all consumers of a method fetch the total set of Include() flavours or you let the consumer specify the include as a functional parameter to the repository method.
So either go for everything - or use something like a specification pattern
@@MilanJovanovicTech You could use the specification pattern but that doesn't necessarily address the issue. Depends on the implementation.🙂
In a real world example a repository method can and should be able to be used by multiple consumers. But rarely they have the exact same needs when it comes to includes.
@@Benke01 Then just let the repository offer multiple variants of the method, either by adding an additional parameter or just create a few extra methods that include specific things.
@@kp_xcess That would quickly escalate to multitudes of methods. And their naming... 😥
Thanks!
Don't mention it :)
Very useful. Hvala :D
Nema na čemu :)
I see in your examples unit of work in command. But if need call multiply commands and after that call unit of work. It is need to do in controller if we speak about mediator ?
I strive to send one command per request
@@MilanJovanovicTech But then if i want to create specific command need to create a lot of diff commands. But better in conroller call multiply small comands and after that call commit
First of all, great video! I really enjoy your work so keep it up!
I am currently working on a clean architecture solution and I'm are quite new in the concept and noticed that you refer the domain layer in infrastructure.
Is it alright that infrastructure can reference to classes in the domain layer or is it only application who have access to this layer? I mean based on the clean architecture diagram, the domain layer is the inner most layer where the application layer is the adjacent layer to domain while presentation and infrastructure are the outer layers.
So im just a bit confused on how outer layers can reference each inner layers without breaking best practices? 😅
Yes, the outer layers can reference the inner layers.
- www.milanjovanovic.tech/blog/clean-architecture-and-the-benefits-of-structured-software-design
- www.milanjovanovic.tech/blog/why-clean-architecture-is-great-for-complex-projects
👋👋👋
What happens when mediator fails to handle your events? They just dissappear, and the initial transaction (your savechanges) is completed! This is not good is it? The change you made in the domain is probably not idempotent, so you can't just run it again and hope the events succeed next time. Shouldn't you save the events along with the entity in a transactional outbox!?
I explained in the Domaim events video this specific problem, and how to solve it
@@MilanJovanovicTech I'll take a look!
@@MilanJovanovicTech This? ruclips.net/video/BimfDeDV4yU/видео.html
Because it only briefly touches the transactional outbox at the end. It seems like you have solution for this without transactional outbox hidden somewhere, but I can't find it, nor understand how it would work. :)
Oh. This one? It is just as problematic as it was before introducing events, only now the chain of events can be basically infinite and crash anytime? Transactional outbox is the only way, as I see it. ruclips.net/video/AHzWJ_SMqLo/видео.html
@@svorskemattias This one ruclips.net/video/XALvnX7MPeo/видео.html 😅😅
How would this behave if an entity had a composite key?
Now you're reaching... I think it's still doable, but the GetById method would need to be updated slightly on the key filtering part
@MilanJovanovicTech
I thought we should only create repositories for aggregate roots in DDD pattern?
And 2nd question, if I want to implement a custom query for an entity which is not an aggregate root, where should I put that
That rule is too constrictive for most applications
Nice one ! Could you make the same Generics Repository pattern /UnitOfWork/ with Stored Procedure and EF! That would be amazing! Thanks
Honestly, you should try avoid using Stored Procedures if possible.
@@Code_Bits can you explain why ? I have UnitOfWork with Generic Repository and I thought with Stored Procedure you can avoid sql injection?!? So what do you suggest?
How will you run into SQL injection with EF?
@@MilanJovanovicTech so what do you suggest? Leave just EF in my project without Stored Procedure? It's better ?
@@attilaguba856 I think the first Google search would answer your question honestly.
Why use UOW pattern when you can just use the DB Set as it already does what the UOW does?
Sigh... Didn't I explain it in the video?
@@MilanJovanovicTech No, I'm talking context. You explain why you use it. I am asking why not use it in a specific way, as all you're doing is recreating functionality that is already available.
Nice video
Thanks!
Hello. How should I implememt select with this pattern. I implemented one but it is complex. Should i implement another overload for select functionality thanks for answer.
Might be better off creating a concrete method for complex queries
Which keyboard are you using?
LOGITECH G413 CARBON
How did you wire the unit of work class
As a scoped service -> services.AddScoped()
@@MilanJovanovicTech i mean doest have to be the same dbcontext, used in the repository class or two scoped instances are ok, one to do the databas access one will do the save changes.
What I had before a method within the abstract reposotory class, this method will do save changes.
Why do we use SingleOrDefault() instead of Find()?
Same result, but I don't like using Find. Prefer writing my LINQ condition
Should Domain project be allowed to reference in Persistence project? As per clean architecture diagram, domain should be referenced in Application project only?
No - it's the other way around. Persistence can reference Application/Domain
@@MilanJovanovicTech ok, but if one may want to keep both layers independent of each other then in which project domain and DB entities should be mapped to each other and what would be appropriate way of mapping them?
CRUD repositorys: 👎🏼 / Domain repositorys: 👍🏼
Fully agree!
hmm so if I am not using EF then how to have a generic repository pattern?
Tough
Hi, is it possible to review your project somewhere (maybe git?)?
I share the source code on Patreon
Could you please project regarding this?
I share the code on my Patreon
awesome! it's quite similar to the approach we've used at my previous work. what do you think of accessing generic repository via a factory or builder of some sort? would it be bad to expose generic repo building based on specified entity type, even if the entity doesn't have an concrete repo?
You can directly use Entities without an concrete repository for them. So you'll only be able to use the Add(), Update() etc... methods for that specific entity. If you implement the repositories the way Milan implemented them you could also make these methods virtual and override them in your specific repositories if needed. At work we also used this implementation of the generic repository pattern and it's a very good way imho. We also used a IUnitOfWorkFactory to create the Unit of Work and retrieving from the dependency injection container the repositories we needed.
How that builder work? Got an example?
@@MilanJovanovicTechI think there were some ideas with exposing Set() DbContext method (where TEntity is IEntity, of course) and some scoped caching of the built repositories in a concurrent collection (if the repo is needed again in the same request)
I personally root against it, since there could be more Entities than DbSets, which could lead to some runtime exceptions
@@MilanJovanovicTech I don't have an example unfortunately, however the Factory created an UnitOfWork instance with the right DbContext. This UnitOfWork instance was used to get repositories (like the Product repository) which only accepted domain entities and automatically mapped them to the DbEntity (in case of an Add()). In case of an any other operation like Update() etc we also passed in a domain entity which would update the DbEntity. The repository took care of mapping them and you only needed to have the mapping configured and the repositories registered in your DI container. This worked very well in our case since as you can guess you only needed to inject the factory within your classes and not the UnitOfWork and repositories.
Great
Thanks!
Is this project code available on git? Thanks!
No, I share the code on my Patreon
First Viewer ❤
Blazing fast, congrats! 🔥
You are just reinventing the wheel. EF is already a repository.
What about code duplication?
@@MilanJovanovicTech Its nice to put an interface around things for many reasons! The repository is not reinvented by doing this. Its just getting a nicer packaging, communicating through specific interfaces what operations are allowed. Also, you gain mockability.
Nice video
Thanks!