Repository Pattern
An abstraction that hides database access behind an interface, keeping business logic independent of storage.
The repository pattern provides an interface for accessing data that hides the underlying storage mechanism. Your business logic works with the interface, not with SQL queries or ORM calls directly.
The key benefit is decoupling. Your application layer defines what data operations it needs (save a user, find orders by status), and the repository implements those operations against a specific database. If you swap PostgreSQL for a NoSQL database, you change the repository implementation. The business logic stays the same.
In Go, this usually means defining an interface in the application layer and implementing it in the adapters layer:
type OrderRepository interface {
Save(ctx context.Context, order Order) error
FindByID(ctx context.Context, id string) (Order, error)
}
The repository works with domain objects, not database rows. It translates between the two worlds. This keeps your domain model clean and free from database-specific annotations or mapping concerns.
Repositories also make testing easier. You can test your business logic with an in-memory implementation while using a real database in integration tests. That said, don't skip testing the real implementation. Mocked repositories can hide actual database issues.
A common mistake is creating one repository per database table. Very often, you need to modify multiple tables within one transaction, and it doesn't make sense to split that logic across multiple repositories. Instead, design repositories around aggregates.
References
- The Repository pattern in Go: a painless way to simplify your service logic — Practical guide to implementing the repository pattern in Go, covering interface design, transaction handling with update functions, and testing benefits.
- Repository secure by design: how to sleep better without fear of security vulnerabilities — Shows how repositories can enforce security constraints at the data access layer, preventing unauthorized access by design.
- How to implement Clean Architecture in Go (Golang) — Explains the repository's role as an adapter in Clean Architecture, decoupling the application layer from database details.
- 4 practical principles of high-quality database integration tests in Go — Covers testing repositories with real databases instead of mocks, ensuring your data access layer works correctly.
- Combining DDD, CQRS, and Clean Architecture in Go — Uses the repository pattern for getting and saving domain objects within command handlers, showing how it integrates with CQRS and DDD.
- Introduction to DDD Lite: When microservices in Go are not enough — Introduces the Repository interface in the domain layer, defining methods like GetOrCreateHour and UpdateHour for working with domain aggregates.
- Microservices test architecture. Can you sleep well without end-to-end tests? — Covers testing strategies for repositories, including unit tests with mocks and integration tests against real databases.
- Database Transactions in Go with Layered Architecture — Explores how to handle database transactions with the repository pattern, keeping transaction logic out of the business layer.
- Common Anti-Patterns in Go Web Applications — Recommends designing repositories around aggregates rather than one per database table, and using the repository pattern to save domain objects transactionally.
- Is Clean Architecture Overengineering? — Discusses the repository pattern as part of the clean architecture approach, referencing the blog post as a key companion piece.
- When you shouldn't use frameworks in Go — Highlights the repository pattern as a timeless, framework-independent approach that makes code easier to work with, even for AI tools.
- Learning Software Skills fast: what worked for us best in the last 15 years — References the repository pattern blog post as an example of timeless software knowledge that compounds over time.
- When it's worth to write low-quality code — Mentions the repository pattern as a practice where high quality doesn't require much extra effort, even in quick prototypes.