Repository pattern
Decouple business logic from data access by encapsulating storage operations behind a collection-like interface, making persistence swappable and testable.
Introduction
The Repository pattern places a domain-facing interface between application logic and persistence. The service asks for orders or saves an order; a concrete repository translates those operations into SQL, a document-store call, an API request, or an in-memory lookup.
TL;DR / mental model: Make persistence look like a collection of domain objects at the service boundary, then keep storage-specific mapping and query mechanics behind that boundary.
Problem and Context
You're building an order management system. The service class needs to find orders, save them, and run queries. Without a pattern, SQL leaks directly into your business logic, and every service method becomes a tangle of domain rules and JDBC boilerplate.
Every time you need a new query, you open this class and paste more SQL. Unit testing applyDiscount requires spinning up a database. Want to move from MySQL to MongoDB? Rewrite every method. The service knows too much about how data is stored.
Here is what changes when you apply the Repository pattern.
When It Helps
Use a repository when business logic needs to be tested independently of storage, persistence details are leaking into services, or the application has a domain model worth protecting. For thin CRUD endpoints, a framework data abstraction may already be enough.
Participants and Structure
OrderService depends on the Repository interface, never on a concrete implementation. InMemoryOrderRepository stores orders in a HashMap for tests. JpaOrderRepository delegates to an EntityManager in production. The Order entity stays clean with no persistence annotations, no SQL, no framework coupling.
A concise summary is: "The repository acts as a collection-like abstraction over the persistence layer. The service talks to the interface, not the database."
Idiomatic Example and Implementation Notes
Continue Reading with Premium
Unlock this article and every other in-depth system design guide on the platform with SDEpedia Premium.
Related Articles
The factory method replaces direct constructor calls with a method that subclasses can override to return different types. Callers depend on an abstraction, not a concrete class.
The strategy pattern extracts a family of algorithms behind an interface so the client can swap behaviors at runtime without touching the context class.
The adapter pattern wraps an incompatible third-party interface so your code can use it through an interface it already expects. Structural bridging without changing either side.