Ir al contenido

Backend

El backend sigue una arquitectura de cuatro capas:

  1. API (backend/app/api/v1/): routers FastAPI, validación de entrada, inyección de dependencias.
  2. Servicios (backend/app/services/): lógica de negocio, reglas y validaciones.
  3. Repositorios (backend/app/repositories/): acceso a datos.
  4. Modelos (backend/app/models/): SQLAlchemy 2.0.

Los servicios se inyectan en los endpoints mediante Depends:

@router.get("/{budget_id}", response_model=BudgetResponse)
async def get_budget(
budget_id: uuid.UUID,
_admin: User = Depends(require_permission("budgets", "leer")),
service: BudgetService = Depends(get_budget_service),
):
return await service.get_budget(budget_id)

Los constructores de servicios reciben repositorios:

class BudgetService:
def __init__(self, repo: BudgetRepository, project_repo: ProjectRepository, ...) -> None:
self.repo = repo
...

BaseRepository (backend/app/repositories/base.py) proporciona operaciones comunes:

  • get_by_id
  • create
  • update
  • soft_delete
  • list_all

Cada repositorio hereda y especializa según el modelo.

El motor async se crea en backend/app/core/database.py:

engine = create_async_engine(settings.DATABASE_URL, echo=settings.DEBUG)
async_session = async_sessionmaker(engine, class_=AsyncSession, expire_on_commit=False)

get_session() se usa como dependencia FastAPI para obtener una sesión por request.

Jerarquía de excepciones en backend/app/core/exceptions.py:

  • DomainError
  • NotFoundError
  • ValidationError
  • ConflictError
  • AuthenticationError
  • AuthorizationError

El handler global en backend/app/main.py:31 convierte DomainError en JSON con el código HTTP correspondiente.

backend/app/core/config.py usa pydantic-settings para cargar variables de entorno.

Pydantic v2 en backend/app/schemas/. Cada módulo define request/response.

Véase Testing.