by Timur Dautov

Repository Design Pattern

The Repository Design Pattern is a structural pattern used to manage data access logic in applications.

It acts as an abstraction layer between the business logic and the data source (e.g., a database, API, or file system).

This pattern helps keep the data access code separate from the core business logic, making the application easier to maintain and test.

Why Use the Repository Pattern?#

  • Decouples Business Logic from Data Access: The business layer doesn’t need to know how the data is stored or retrieved.
  • Encapsulates Query Logic: Centralizes data access logic in one place.
  • Improves Testability: Allows mocking repositories for unit testing.
  • Supports Multiple Data Sources: Makes it easier to switch databases or storage mechanisms.

Components of the Repository Pattern#

  1. Repository Interface: Defines the contract for data access operations.
  2. Concrete Repository: Implements the repository interface and provides the actual data access logic.
  3. Entity: Represents the domain object being stored or retrieved.
  4. Data Source: The underlying storage mechanism (e.g., database, API).

Example of Repository Pattern in Typescript#

interface Repository<T> {
  getById(id: number): T | undefined;
  getAll(): T[];
  add(entity: T): void;
  update(entity: T): void;
  delete(entity: T): void;
}

class InMemoryRepository<T> implements Repository<T> {
  private data: T[] = [];

  getById(id: number): T | undefined {
    return this.data.find((entity) => entity.id === id);
  }

  getAll(): T[] {
    return this.data;
  }

  add(entity: T): void {
    this.data.push(entity);
  }

  update(entity: T): void {
    const index = this.data.findIndex((e) => e.id === entity.id);
    if (index !== -1) {
      this.data[index] = entity;
    }
  }

  delete(entity: T): void {
    this.data = this.data.filter((e) => e.id !== entity.id);
  }
}

class User {
  constructor(public id: number, public name: string) {}
}

const userRepository = new InMemoryRepository<User>();

userRepository.add(new User(1, 'Alice'));
userRepository.add(new User(2, 'Bob'));

const user = userRepository.getById(1);
console.log(user); // User { id: 1, name: 'Alice' }

In this example, we define a generic Repository interface with common CRUD operations. We then implement an InMemoryRepository class that stores entities in memory. We create a User class as an example entity and use the repository to add, update, and retrieve users.

The Repository Design Pattern is a powerful tool for managing data access in applications, providing a clean separation between business logic and data storage.