Skip to main content

Serko Northsky Backend Architecture

This guide explains the Serko Northsky backend structure, the layered architecture pattern, and how components interact.

Directory Structure

apps/backend/src/serko_northsky/
├── api/ # FastAPI app, routers, middlewares
├── containers/ # Dependency injection and config providers
├── core/ # Business logic services and orchestrators
├── infra/ # Infrastructure (repositories, persistence, UoW)
├── interfaces/ # Abstract interfaces for services and repositories
└── models/ # Pydantic API models and SQLAlchemy entities

Architectural Layers

┌─────────────────────────────────────────────────────────────┐
│ API Layer (Routers) │
│ Handles HTTP requests, validation, response mapping │
├─────────────────────────────────────────────────────────────┤
│ Service Layer (Core) │
│ Business logic, domain rules, orchestration │
├─────────────────────────────────────────────────────────────┤
│ Infrastructure Layer (Infra) │
│ Repositories, Unit of Work, external integrations │
├─────────────────────────────────────────────────────────────┤
│ Data Layer (Models) │
│ SQLAlchemy entities, Pydantic schemas │
└─────────────────────────────────────────────────────────────┘

Key Patterns

Unit of Work (UoW)

The UnitOfWork pattern provides transaction boundaries and repository access.

See Unit of Work for detailed documentation.

Dependency Injection

Services are constructed with dependencies via the DI container.

ComponentSource
Containercontainers/api_container.py
Dependenciescontainers/dependencies.py

Repository Pattern

Repositories abstract database operations behind interfaces. See SqlRepository for the base implementation.

RepositorySource
Baseinfra/repositories/sql_repository.py
Userinfra/repositories/user_repository.py
Tripinfra/repositories/trip_repository.py

Service Layer

Services contain business logic and use repositories via UoW. See core/services/ for implementations.

Guidelines

  • Inject BaseUnitOfWork in the service constructor
  • Use repository methods from uow to read/write entities
  • Call await uow.save_changes() after write operations
  • Call await uow.flush() only when bypassing repositories

Orchestrators

Orchestrators coordinate multi-step business workflows. See core/orchestrators/ for implementations.

OrchestratorPurpose
ChatOrchestratorAI conversation flow
FlightSearchOrchestratorQPX flight search
AccommodationSearchOrchestratorHotel search (Booking.com + Sabre)

When to use orchestrators:

  • Workflow spans multiple bounded contexts
  • Non-trivial business logic sequencing
  • Consistency rules across aggregates

When NOT to use:

  • Simple CRUD within a single aggregate
  • Single service operations

Router Layer

Routers handle HTTP I/O and dependency injection. See api/v1/routers/ for all endpoints.

AI Agent Integration

The backend exposes tools via PydanticAI agents for conversational trip planning. See core/agents/ for agent implementations.

AgentPurposeLocation
Context AgentExtract trip requirementscore/agents/context_agent/
Conversational AgentUser dialoguecore/agents/conversational_agent/
Trip Planner AgentSearch executioncore/agents/trip_planner_agent/

See AI Orchestration for detailed documentation.

info

Agent tool handlers are NOT request-scoped. They must explicitly create and manage their own Unit of Work using the factory.

Adding a New Feature

  1. Define entities in models/database_entities/ if needed
  2. Create repository in infra/repositories/ inheriting from SqlRepository[Entity]
  3. Define service interface in interfaces/core/services/
  4. Implement service in core/services/ injecting BaseUnitOfWork
  5. Add router endpoints in api/v1/routers/ with DI
  6. Register router in api/v1/api.py