How can I use the repository pattern correctly?
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
The repository pattern is useful when it gives your application a clean boundary around persistence. It is not useful when it becomes a second API that simply mirrors every ORM method with more boilerplate.
Using the repository pattern correctly means being selective. A repository should expose domain-oriented data access operations for aggregate roots, not act as a generic dump of every query and transaction concern in the system.
What a Repository Is Supposed to Do
A repository gives the rest of the application a collection-like interface for loading and saving domain objects. The service layer should not need to know whether the data comes from SQL, an ORM, an API call, or something else.
That idea is easiest to see with an interface:
The interface is small and domain-focused. It does not expose random persistence details the rest of the application should not care about.
Keep Business Logic Out of the Repository
Repositories are for persistence concerns, not business rules. A service can use the repository and enforce domain behavior:
This separation matters. If the repository starts deciding whether emails are valid, whether registration is allowed, and how workflows behave, it stops being a repository and becomes a confusing mixture of concerns.
Do Not Wrap the ORM Blindly
The most common misuse of the repository pattern is creating one repository method per ORM method:
- '
filter' - '
order_by' - '
limit' - '
join' - '
update'
At that point, the repository adds almost no abstraction. It just re-exposes the underlying data tool with extra indirection.
The better question is: which queries does the domain actually need? Define those. Skip the rest.
For example, get_active_subscription_for_user(user_id) is a repository-level method with domain meaning. filter_by_status_and_sort_descending often is not.
Repositories Usually Belong Around Aggregate Roots
In domain-driven design, repositories are most natural for aggregate roots, not for every table. That helps keep the boundary meaningful.
If you create a repository for every small entity, you often end up with:
- too many tiny abstractions
- difficult transaction coordination
- service code that has to orchestrate several repositories for one use case
A repository should fit the shape of your domain, not just the shape of the database schema.
Testing Is a Benefit, Not the Only Goal
Repositories can make testing easier because services depend on a small interface rather than direct database code. But testing alone is not a reason to add a repository layer if it adds no useful abstraction.
Use the pattern when it clarifies boundaries and keeps persistence details from leaking across the application. Do not add it just because "architecture says every project must have repositories."
Common Pitfalls
- Mirroring the ORM API instead of designing domain-oriented repository methods.
- Putting business rules inside repositories.
- Creating repositories for every table instead of around meaningful aggregates.
- Letting transaction management spread awkwardly across several tiny repositories.
- Adding a repository layer that provides no abstraction and only adds boilerplate.
Summary
- Use repositories to hide persistence details behind domain-focused operations.
- Keep business rules in services or domain objects, not in repositories.
- Avoid wrapping every ORM method one for one.
- Design repositories around aggregate roots and real use cases.
- The repository pattern is valuable when it simplifies boundaries, not when it becomes decorative architecture.

