Repository Pattern: Library Catalogue

The Repository Pattern is a design pattern that provides a clean way to manage data access logic while keeping your business logic unaware of how data is stored or retrieved. Rather than working directly with a database, your application talks to a repository — a dedicated class responsible for handling queries, saving changes, and retrieving results.

This pattern promotes separation of concerns and helps you write cleaner, testable, and more maintainable code. It’s especially useful in enterprise applications where you want to abstract away the complexities of Entity Framework, SQL, or any external data source.

Real-Life Analogy: Library Catalogue

Think of a library. When you want to borrow a book, you don’t go wandering through the shelves hoping to find it. Instead, you search the catalogue or ask the librarian. The librarian knows exactly where the book is, whether it’s checked out, and how to find it.

In this analogy:

  • Repository = The librarian who handles the book search and fetch
  • Model = The book itself
  • Caller/Service = You, requesting the book but not caring where it comes from
Library Catalogue analogy for Repository Pattern
With a repository, your code asks for what it wants without knowing how the data is organised or stored — just like asking a librarian instead of exploring shelves yourself.

Benefits of the Repository Pattern

By using a repository, you create a consistent interface for querying and manipulating data. This makes your application easier to test and change, since your business logic doesn’t rely directly on the database or ORM.

It also allows you to switch out your data source (SQL, NoSQL, in-memory, mock, etc.) without rewriting your core logic.

  • Abstraction of data access logic behind a clear interface
  • Testability by mocking the repository for unit testing
  • Centralised logic for queries and data operations
  • Cleaner architecture with separation between data and business concerns

What to Implement

A Repository interface defines the operations, and a concrete class implements them using a specific data source (e.g. EF Core). Your service or controller then depends only on the interface.

  • IRepository<T>: Generic interface for common CRUD operations
  • ConcreteRepository: Implements the interface using EF Core, SQL, or other tech
  • Consumer: A service or controller that uses the repository

How It Works in C#

// Model
public class Book {
    public int Id { get; set; }
    public string Title { get; set; }
}

// Interface
public interface IBookRepository {
    Book GetById(int id);
    IEnumerable<Book> GetAll();
    void Add(Book book);
    void Delete(int id);
}

// Implementation
public class BookRepository : IBookRepository {
    private readonly LibraryContext _context;
    
    public BookRepository(LibraryContext context) {
        _context = context;
    }

    public Book GetById(int id) => _context.Books.Find(id);

    public IEnumerable<Book> GetAll() => _context.Books.ToList();

    public void Add(Book book) {
        _context.Books.Add(book);
        _context.SaveChanges();
    }

    public void Delete(int id) {
        var book = _context.Books.Find(id);
        if (book != null) {
            _context.Books.Remove(book);
            _context.SaveChanges();
        }
    }
}

// Usage (in a service)
public class LibraryService {
    private readonly IBookRepository _repo;

    public LibraryService(IBookRepository repo) {
        _repo = repo;
    }

    public void DisplayBook(int id) {
        var book = _repo.GetById(id);
        Console.WriteLine($"Book title: {book?.Title}");
    }
}

When Should You Use It?

Use the Repository Pattern when:

  • You want to decouple your application logic from data access
  • You’re using Entity Framework or a similar ORM
  • You need to write unit tests without hitting the real database
  • You expect your data access implementation to evolve over time

When Not to Use It: For small apps or simple queries, it might add unnecessary complexity. If you only have a few database operations, using EF Core or Dapper directly can be simpler.

Where Is It Used in the Real World?

The Repository Pattern is widely used in enterprise-level .NET applications, especially those that rely on domain-driven design (DDD) or aim for a clean separation between the core logic and infrastructure.

  • ASP.NET Core applications: Especially when using Entity Framework for database operations
  • Domain-driven design (DDD) projects: Repositories encapsulate data access for aggregates
  • Microservices: Each service often manages its own repository for persistence
  • Testing environments: Use in-memory or mock repositories to simulate data behaviour

Final Thoughts

The Repository Pattern gives you a clean interface between your application and the data layer. It’s not just a way to write less code — it’s a way to write better, more maintainable code.

If your app is starting to grow and data access logic is leaking everywhere, it might be time to introduce repositories — just like you’d ask the librarian instead of roaming the shelves yourself.