Repository pattern in ASP.NET (.NET8) with some additional good practises
What are those design patterns in programming, and why do we need them?
In short design patterns are a repeatable solution to commonly occurring problems in code design. It is a description or template for how to solve a problem that can be used in many different situations.
Patterns offer established best practices, structuring code efficiently, maintaining consistency, saving time, aiding scalability and especially enhancing communication among developers.
Repository pattern
The pattern idea is that the data access code should be separated from the business logic code. The Repository Pattern provides a way to manage data access code in a separate layer, thus improving code maintainability.
Why do we need to use a repository pattern?
- Layers independency.
- Reusability of layers and classes.
- When writing logic, use repository interfaces for data retrieval. Each layer has a unique responsibility, ensuring atomic methods.
- Helps to maintain code readability.
- Interface-driven design alongside repository pattern allows to easily change implementations.
Out of curiosity, I googled the meaning of the word “Repository” and it resulted this:
A place where or receptacle in which things are or may be stored. F.e.: “a deep repository for nuclear waste”
Layers using repository pattern
I like to separate more application layers than just repository for even better code efficiency, maintainability and so on. So great addition to this pattern would be to separate the API layer from business logic, and business logic layer from repository.
The dependencies would look like this:
- “API” layer → “Business” layer.
- “Business” layer → “Repository” layer.
- “Repository” layer → “Service client” layer. (To use external project APIs in our project, we can introduce an additional “Service client” layer. This layer will specifically collect data from third-party APIs)
Responsibilities of each layer in short:
“API” → This is where application steering wheel and engine go. This layer is responsible for communicating with the app. We need it because there is no point in having business logic if we can’t use it. This layer can have controllers for API calls, hubs for websockets, middleware (f.e.: Exception middleware), ‘Swagger’ documentation and so on.
“Business” → The brain of application. Must have business services, where all business logic goes on.
“Repository” → It just gets the data that application needs. Must have repository services to get required data (f.e.: from database, service client).
“Service client” → Gets data that repository needs from third party services. Must have service client, to get required data from others API’s (f.e.: get data from ‘Google Maps API’).
Make sure that you refrain from taking any actions involving layers that aren’t part of their dependencies. For instance, only the business layer is permitted to make one-way requests to the repository layer, and no other layer should do so. Since the repository layer doesn’t rely on the business layer, it cannot make any requests to the business layer.
Interface usage
To ensure the isolation of data access code from other layers, a strategy is to designate every repository class as internal, preventing access from other layers. However, this approach raises the question of how business services can communicate with the repository. The solution is straightforward: utilize interfaces. This same principle can be applied to interactions between other layers.
While focusing on single responsibilities, we can implement dependency injections across layers instead of writing them into a single file. Here is how it’s done:
Now after we have done this we just add the interfaces we need in each service constructor. Here is an example:
Here you go. Congratulations on implementing the repository pattern!
Bits of Advice:
- If there is a requirement for commonly used code across multiple layers, such as string extensions, establishing a “Core” layer would serve as a dependency for all other layers.
- Ensure that you avoid inadvertently using any restricted layers by reviewing your dependencies in the .csproj files.
Thank you for taking the time to read this article. I encourage you to share your insights to aid in enhancing future articles!
Next suggested article: