Where Should You Put Repository Interfaces In Clean Architecture?

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
where should you place repository interfaces in a domain-centric architecture one example of a domain Centric architecture is the clean architecture and in this video I'm going to talk about defining your repository interfaces in the domain layer we're starting from an implementation that's using a database context abstraction to query records from the database add new records and persist changes in the database the abstraction I'm talking about is the I application DB context which is just a wrapper around EF course database context you can see we're using it to perform link queries to fetch records from the database and also to add new entities and lastly to persist changes by calling save changes async now I want to refactor this to use a repository approach and I'm going to start off by defining my repositories in the domain layer we have to first identify defy which repositories we want to create you'll notice that we are working with customers over here orders over here and then order summaries so that's going to require three separate repository interfaces now for the customers we need a method for fetching a customer by the ID and for the order and Order summaries repositories we just need methods for inserting new objects we're going to take care of the call to save changes later on so I'm going to define the repository interfaces in the domain layer and I'm going to explain what are the benefits behind this approach so I'm going to start off by creating an I order Repository and it's just going to have one method which is going to be for it it's going to be called add and we're going to be accepting an order instance so we are adding a new order to the repository In The Same Spirit let's also quickly create the I order summary repository so I order summary and it's going to accept an order summary instance instead of an order and let's also fix the naming so I'm going to be moving these into separate files but let's just look at them side by side for a moment even though we are defining the repository interfaces in the domain layer we still aren't introducing any external dependencies related to persistence such as EF core in this example the repository interface is only there to expose a sort of a public API to the application layer and tell it what it can do with the domain so in this case we only want to expose methods for inserting these entities to the database or the repository and we also need to create a repository for our customer so this is going to be the I customer repository let me just type this out and we're also going to make it a public interface and it's going to have one method which is going to be asynchronous so it's going to return a task of customer and we're going to make it nullable and let's call it get by ID async now the autocomplete is offering me to use a guide identifier and this is incorrect because I'm using a strongly typed customer ID value to represent my customer ID I like the expressiveness of using strongly typed IDs in my repositories and added value of this approach is that you get compile time safety because you're using a strongly typed ID now with the repository interface is in place let's go back to our create order command Handler and let's see how this would look like if we were to use repositories I'm going to add fields for the repositories that we want to Define so I first need the I customer repository and then I'm going to need the order repository and lastly I'm going to need the I order summary repository let's inject all of them from the Constructor and and I'm going to fix the alignment here so that you can clearly see what's going on in our Constructor now with that in place let's head over to the handle method we want to replace the link queries with our repository calls so let's approach it one by one so this becomes customer repository get by ID async right here instead of calling context orders add we're going to call order repository add and the same applies here instead of calling context order summaries we're going to call the order summary repository what should we do with the call to save changes to completely get rid of the dependency on the database context so we can introduce a unit of work abstraction and I'm not going to place this in the domain layer rather I'm going to Define it in the application layer because I think it makes more sense to place it here rather than in the domain layer so this is going to be the I you it off work and I'm going to copy the method that is in the I application DB context interface which is this one for persisting changes in the database and let's use it in our unit to work now if we go back to our create order command Handler and we introduce the last dependency that we need which is the i unit of work and we also need to inject it from The Constructor now we can completely replace the eye application DB context dependency so this now becomes unit of work save changes async and you'll see that the I application DB context is unused so let's get rid of it and also let's get rid of it from The Constructor and this is what we are left with there are good sides and there are bad sides to this approach the good side is we are no longer depending on EF core we are depending on our own abstractions which encapsulate EF core behind the scenes so we replace the EF core dependency we also have the flexibility of using various implementations behind the scenes for example we could combine EF core with raw SQL and something else if we want to and it would be completely abstracted away from our Command handlers one more benefit is also testability because it's easier to provide mocks for the repository interfaces than it is for EF core even though you shouldn't be mocking your database interactions it's probably better that you use a real database it makes sense to Mark these dependencies if all you're doing is just testing the logic in your handlers in this case let's say instead of returning here we throw some X exception or returning a failure result it would make sense to Mark the repositories and just make sure that this logic is performing as expected let's also take a look at the remove line item command Handler and see what we can do here again we are using the application DB context to query the order we are also including a single line item with a filtered include statement and we are removing the line item and proceeding the changes how we would approach this using repositories is only with the order repository so let's add that field so order repository and we need our i unit of work to process changes and the so let's inject both of these from The Constructor I'm going to just align this vertically and let's head over to the handle method now the query here for fetching the order is going to become a single call to the repository we don't have a method yet so let's head back to our order repository and Define this method we could do something straightforward like let's say a method returning a nullable order and just call it get by ID async pass it to the strongly typed order ID and we're done the problem that we're going to have is that the remove line item command Handler is using a filtered include statement in the query to only fetch a single line item from the database so we could just create a more specific method on our repository let's say get by ID with line item this should tell the color of this method that it's also going to fetch a line item inside of the order aggregate so we can additionally pass online Item ID to perform the required filter with this in place we can head over to the remove line add-on command Handler and we can replace all of this with just a call to order repository and we call the getby ID with line item method and we're going to pass it the order ID and the line item ID so I'm going to just align these like this so that you can see everything on the screen and the last thing that's left to replace is the call to context save changes it now becomes unido work save changes so let's get rid of our application DB context and also from The Constructor and now our remove line item command Handler no longer depends on EF core and we are left with the implementation that's only relying on the order repository and the unit of work if we leave our implementation like this we're going to get a runtime exception because we didn't actually Implement any of these interfaces so let's add the implementation inside of the persistence layer and let's for example create a folder that is going to hold our repositories and let's define them one by one so I'm going to start by adding the order repository and we're going to make this class internal and sealed and it has to implement the I order repository interface now let's add these two methods I'm going to use the application DB context in order to implement this so let's add our context inject it from The Constructor and now the implementations become straightforward we're just going to say context orders add and we pass in the order that is here and here we just need to retype the query that we had in the previous remove line and command Handler I'm going to call the database context we're going to query the orders let's add an include statement for the order aggregate then we want to include the orders line items and we want a filtered include where the line item id matches the line item ID that is passed to this method and we of course want to call the single or default async method and Define an additional filter on the order to match by the order ID I'm not calling async await here because it's not really necessary it's going to be awaited anyway inside of the command Handler this takes care of the order repository let's also add the order summaries Repository this one is going to implement the respective I order summary interface that we defined earlier and it only has one method I'm going to define a field for our application DB context and we're going to inject it from The Constructor and we just have to say context order summaries and we have to add the order summary that is passed to this method and all that's left is the customer repository with a single method inside so repository we have to implement the I customer repository interface I'm going to add a field for the application DB context and I'm going to inject it from The Constructor for the implementation I'm going to say return context customers and let's call single or default async and we're going to filter that the customer ID is equal to the one that we passed through to repository so this completes the repository implementations and we also have to add the unit to work now I can just go ahead and implement the unit of work interface directly in the application DB context and you'll see that nothing is broken here this is because I already implemented the same method in the I application DB context so there's nothing additional to do but we do have to wire up our dependency injection so I'm going to do this and instead of I application DB context I'm going to provide the unit of work and let's also add all of our repositories and Scope Services so we had the I customer repository and the customer repository implementation then we had the order repository so I order repository and Order repository as the implementation and lastly we have the I order summary repository and the order summary repository implementation you can see that we are keeping the repository implementations as internal classes and we can wire up dependency injection from the persistence project directly and we don't have to expose the implementation details anywhere else just to test out that this is working properly let's go through the flow of creating an order and see how everything is functioning so I'm going to send a post request to our API to create a new order we hit the breakpoint at the start of the handle method and we go inside of our customer repository to fetch the customer with the given ID so this is going to return back a customer instance that we're going to use to create a new order and then we have to insert the order using the order repository and the same applies for the order summary repository lastly we're going to process changes by calling the unit of work save changes async method and this is going to persist our order in the database and we can return the response to postman what I just showed you is the specific repository pattern and it's my preferred approach when using domain driven design because I can Define the repository interfaces in the domain layer and expose only the methods that are necessary for working with my domain the application layer can only call the methods exposed by these repositories this only covers command or writing to the database I'm going to handle querying the database in a separate video If you enjoyed this video make sure to smash that like button and also subscribe to my channel so that you don't miss the future videos take a look at the video that you can see right here and until next time stay awesome
Info
Channel: Milan Jovanović
Views: 9,133
Rating: undefined out of 5
Keywords: repository, repository pattern, repository design pattern, repository pattern c#, repository pattern in asp.net core web api, repository pattern c# mvc, repository pattern in asp.net core, repository pattern in mvc, repository pattern c# web api, repository pattern and unit of work with asp.net core 5, design pattern, design patterns, .net 6, dotnet, .net 5, .net 7, cqrs, entity framework, repository pattern with entity framework, repository pattern with entity framework core
Id: rG090b5MRGo
Channel Id: undefined
Length: 15min 17sec (917 seconds)
Published: Tue May 16 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.