DEV Community

Cover image for Repository Pattern: Simplify Data Access in Enterprise Apps
AHMED HASAN AKHTAR OVIEDO
AHMED HASAN AKHTAR OVIEDO

Posted on

Repository Pattern: Simplify Data Access in Enterprise Apps

Introduction

Enterprise applications power modern businesses. They manage vast amounts of data, automate workflows, and support critical systems like banking, logistics, and HR. These apps face unique challenges: handling complexity, ensuring scalability, and maintaining clean architecture.

Martin Fowler's Patterns of Enterprise Application Architecture (2002) is a cornerstone. It catalogs patterns from real-world systems, covering layers, domain logic, and data mapping. Among them, the Repository Pattern stands out for data access.

Why It Matters?

Data is king in enterprise apps. The Repository Pattern decouples business logic from data storage. It acts as a mediator, providing a uniform interface for CRUD operations. This promotes testability, flexibility, and maintainability. Without it, code becomes tangled, hard to change.

Today, we dive into the Repository Pattern with a Python example. Let's build an employee management system.


What is the Repository Pattern?

It encapsulates data access. Abstracts CRUD operations (Create, Read, Update, Delete) into a collection-like interface. Domain objects don't touch the DB directly; they use the repository.

Key Benefits:

  • Clear Separation: Pure business logic, no infrastructure ties.
  • Easy Testing: Mock repositories for unit tests.
  • Flexibility: Switch DB (SQL to NoSQL) without breaking code.
  • Reusable: Common interface for entities.

Practical Example in Python

Employee management system. We use an in-memory list (easy to adapt to real DB).

Employee Entity:

This class represents an employee in our domain. It holds basic attributes like ID, name, position, and salary. The __repr__ method provides a readable string representation for debugging and logging.

class Employee:
    def __init__(self, id: int, name: str, position: str, salary: float):
        self.id = id
        self.name = name
        self.position = position
        self.salary = salary

    def __repr__(self):
        return f"Employee(id={self.id}, name='{self.name}', position='{self.position}', salary={self.salary})"
Enter fullscreen mode Exit fullscreen mode

Repository Interface:

Defines the contract for data access. Uses abstract methods to ensure any implementation (in-memory, SQL, etc.) follows the same interface. This abstraction allows swapping storage without changing business logic.

from abc import ABC, abstractmethod
from typing import List, Optional

class EmployeeRepository(ABC):
    @abstractmethod
    def add(self, employee: Employee) -> None: pass
    @abstractmethod
    def get_by_id(self, id: int) -> Optional[Employee]: pass
    @abstractmethod
    def get_all(self) -> List[Employee]: pass
    @abstractmethod
    def update(self, employee: Employee) -> None: pass
    @abstractmethod
    def delete(self, id: int) -> None: pass
Enter fullscreen mode Exit fullscreen mode

In-Memory Implementation:

A concrete repository using a list. Manages employee data in memory. add assigns a unique ID. get_by_id finds by ID. get_all returns a copy to avoid mutations. update replaces the employee. delete removes by ID. Simple, but demonstrates the pattern.

class InMemoryEmployeeRepository(EmployeeRepository):
    def __init__(self):
        self._employees = []
        self._next_id = 1

    def add(self, employee: Employee) -> None:
        employee.id = self._next_id
        self._next_id += 1
        self._employees.append(employee)

    def get_by_id(self, id: int) -> Optional[Employee]:
        return next((emp for emp in self._employees if emp.id == id), None)

    def get_all(self) -> List[Employee]:
        return self._employees.copy()

    def update(self, employee: Employee) -> None:
        for i, emp in enumerate(self._employees):
            if emp.id == employee.id:
                self._employees[i] = employee
                break

    def delete(self, id: int) -> None:
        self._employees = [emp for emp in self._employees if emp.id != id]
Enter fullscreen mode Exit fullscreen mode

Business Service:

Contains business logic. Depends on the repository interface, not implementation. Methods like hire_employee create and add employees. promote_employee updates position and salary. This layer focuses on rules, not data storage.

class EmployeeService:
    def __init__(self, repository: EmployeeRepository):
        self.repository = repository

    def hire_employee(self, name: str, position: str, salary: float) -> Employee:
        employee = Employee(0, name, position, salary)
        self.repository.add(employee)
        return employee

    def get_employee(self, id: int) -> Optional[Employee]:
        return self.repository.get_by_id(id)

    def list_employees(self) -> List[Employee]:
        return self.repository.get_all()

    def promote_employee(self, id: int, new_position: str, new_salary: float) -> bool:
        employee = self.repository.get_by_id(id)
        if employee:
            employee.position = new_position
            employee.salary = new_salary
            self.repository.update(employee)
            return True
        return False

# Demo
if __name__ == "__main__":
    repo = InMemoryEmployeeRepository()
    service = EmployeeService(repo)

    emp1 = service.hire_employee("Alice Johnson", "Developer", 75000.0)
    emp2 = service.hire_employee("Bob Smith", "Manager", 90000.0)

    print("Employees:")
    for emp in service.list_employees():
        print(emp)

    service.promote_employee(emp1.id, "Senior Developer", 85000.0)
    print("\nAfter promotion:")
    print(service.get_employee(emp1.id))
Enter fullscreen mode Exit fullscreen mode

Result: Modular, testable, scalable code. Business logic is separate from data access, making it easy to test services with mocks and switch repositories.


Conclusion

The Repository Pattern transforms enterprise apps: cleaner, more flexible, maintainable. Try in Python or adapt to your stack.

Read More: Repository in Fowler


References

Top comments (0)