Enterprise Design Pattern - Repository
Design Pattern
10 Articles
In this article, let's learn about Repository Design Pattern
in .NET.
Table of Contents
- Introduction
- What is Repository Pattern?
- Implementing a Repository Pattern
- Generic Repository
- Non Generic Repository
- Combining Generic and Non Generic Repository
- FAQs
- Summary
Introduction
The Repository pattern
is a commonly used enterprise design pattern
,
especially in enterprise applications. It helps to abstract and encapsulate data access
logic, promoting a
clean separation of concerns between business logic and data access logic.
What is Repository Pattern?
The Repository pattern provides a way to manage data access with minimal code duplication
and
improved testability
. It abstracts the data layer, making the business logic unaware of the underlying data
source. This allows developers to switch persistence technologies, if needed, without impacting the business logic. Repository Pattern in other
words allows developer to have a specific persistence
for each repository based on need.
Instead of directly interacting with the database using tools like ADO.NET or Entity Framework Core, the business logic communicates with repositories. These repositories provide a set of methods to perform CRUD (Create, Read, Update, Delete) operations on data entities.
Implementing Repository Pattern
A typical repository starts with a contract, often defined as an interface
. This
interface
defines common data operations that can be reused across different entities, reducing redundancy
in your codebase. For example:
Code Sample - Generic Repository
Generic Repository
A generic repository
allows you to define common
CRUD operations once and
reuse
them across multiple entities, reducing code duplication
. In this
example, lets take EF Core as the ORM and define a generic repository that implements the IRepository interface
.
Code Sample - Generic Repository Implementation
Advantages
Reusability
: Common data operations are defined once and reused across multiple entities.Consistency
: All entities have a consistent API for CRUD operations.
Disadvantages
Lack of Specificity
: May not provide methods tailored to specific entities, requiring additional custom methods.Over-generalization
: Can become too abstract, making it difficult to implement entity-specific logic.
Non Generic Repository
Non-generic repositories
are tailored to specific entities. They define custom
methods that are unique to the entity they represent. But also have the same methods defined in the generic repository
.
For example:
Code Sample - Non Generic Repository
Advantages
Specificity
: Tailored to the needs of specific entities, making them more flexible for complex queries and operations.Clear Intent
: The repository's purpose is clear, as it deals with a specific entity.
Disadvantages
Potential Code Duplication
: Common CRUD operations may be repeated across different repositories.Maintenance Overhead
: Managing multiple repositories can become cumbersome in large projects.
Combining Generic and Non Generic Repository
In practice, you can mix both approaches
. Use a generic repository
for
common operations
and a non-generic
repository for
entity-specific
methods. All we need to do is to create a non-generic repository that inherits
from the generic repository and implements the entity-specific methods
. This way, we can reuse the common CRUD operations
defined in the generic repository and add custom methods as needed reducing massive code duplication.
Code Sample - Combining Generic and Non Generic Repository
Code Sample - Combining Generic and Non Generic Repository Implementation
FAQs
Entity Framework Core and the Repository Pattern
Entity Framework Core (EF Core)
can be used with the Repository pattern to simplify data access. While
EF Core provides a built-in repository pattern, you can choose whether to use it directly or implement your own.
Should You Use EF Core's Built-In Repository?
Entity Framework Core
provides a repository implementation via the DbSet
.
But it is considered a bit leaky
, so it's often encapsulated
in another
repository just as we did in the demos.
Multiple SaveChangesAsync()?
The problem with multiple SaveChangesAsync()
calls in each respository is that they can lead to
inconsistent data states
. To avoid this, consider using a Unit of Work
pattern to manage transactions
across repositories which we will cover in next article.
Summary
The Repository
pattern, whether generic
,
non-generic
, or a combination
, is a powerful tool for managing data access in a
clean, maintainable
way. It enhances testability, flexibility, and maintainability
,
allowing your application to adapt as requirements change. But, we've currently got those SaveChanges
called in
our repositories, does such a method really belong there? And what if we need to work with transactions
across
repositories? That is where the Unit of Work pattern comes in. Let's have a look at that one next.