Pattern-001: Repository Pattern

Status

Proven

Context

Data access logic scattered throughout the codebase creates maintenance challenges and tight coupling between domain models and database implementation. The Repository Pattern addresses:

Pattern Description

The Repository Pattern encapsulates data access logic and provides a clean interface between domain models and database implementation, with automatic resource management and consistent transaction handling.

Core concept:

Implementation

Structure

class BaseRepository:
    """Base repository with common CRUD operations"""

    def __init__(self, session: AsyncSession):
        self.session = session

    async def create(self, **kwargs) -> Any:
        # Use transaction context for automatic commit/rollback
        async with self.session.begin():
            instance = self.model(**kwargs)
            self.session.add(instance)
            # Automatic commit via context manager
        return instance

    async def get_by_id(self, id: str) -> Optional[Any]:
        result = await self.session.execute(select(self.model).where(self.model.id == id))
        return result.scalar_one_or_none()

Domain-Specific Implementation

class ProjectRepository(BaseRepository):
    """Domain-specific repository"""

    model = ProjectDB

    def __init__(self, session: AsyncSession):
        super().__init__(session)

    async def list_active_projects(self) -> List[Project]:
        result = await self.session.execute(
            select(ProjectDB).where(ProjectDB.is_archived == False)
        )
        db_projects = result.scalars().all()
        return [db_project.to_domain() for db_project in db_projects]

Usage with Session Management

from contextlib import asynccontextmanager
from services.database.session_factory import AsyncSessionFactory

async def service_example():
    """Example service using context manager for automatic resource management"""
    async with AsyncSessionFactory.session_scope() as session:
        repo = ProjectRepository(session)
        return await repo.list_active_projects()

Usage Guidelines

Benefits

Trade-offs

References

Migration Notes

Consolidated from: