forked from cleveragents/cleveragents-core
2.6 KiB
2.6 KiB
ADR-007: Repository Pattern for Persistence
Status
Accepted
Context
The discovery phase identified complex persistence needs:
- 122 data structures to persist
- Multiple storage backends (PostgreSQL, MySQL, SQLite, in-memory)
- Git-based diff storage
- Need for testability with mock repositories
- Transaction management across operations
Decision
We will implement the Repository pattern with abstract interfaces in the domain layer and concrete implementations in the infrastructure layer.
Domain Interface
# cleveragents.domain.repositories
from abc import ABC, abstractmethod
from typing import Optional, List
class PlanRepository(ABC):
@abstractmethod
async def create(self, plan: Plan) -> Plan:
pass
@abstractmethod
async def get(self, plan_id: str) -> Optional[Plan]:
pass
@abstractmethod
async def update(self, plan: Plan) -> Plan:
pass
@abstractmethod
async def delete(self, plan_id: str) -> bool:
pass
@abstractmethod
async def list_by_project(self, project_id: str) -> List[Plan]:
pass
Infrastructure Implementation
# cleveragents.infrastructure.repositories
from sqlalchemy.ext.asyncio import AsyncSession
class SQLAlchemyPlanRepository(PlanRepository):
def __init__(self, session: AsyncSession):
self.session = session
async def create(self, plan: Plan) -> Plan:
db_plan = PlanModel.from_domain(plan)
self.session.add(db_plan)
await self.session.commit()
return db_plan.to_domain()
Unit of Work Pattern
class UnitOfWork:
def __init__(self, session_factory):
self.session_factory = session_factory
async def __aenter__(self):
self.session = self.session_factory()
self.plans = SQLAlchemyPlanRepository(self.session)
self.contexts = SQLAlchemyContextRepository(self.session)
return self
async def __aexit__(self, exc_type, exc_val, exc_tb):
if exc_type:
await self.rollback()
await self.session.close()
async def commit(self):
await self.session.commit()
async def rollback(self):
await self.session.rollback()
Consequences
Positive
- Clean separation between domain and persistence
- Easy to test with in-memory implementations
- Supports multiple storage backends
- Transaction boundaries are explicit
Negative
- More boilerplate code
- Potential for anemic domain models
- Mapping overhead between layers
References
- Martin Fowler's Repository Pattern
- Domain-Driven Design by Eric Evans