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.
| Component | Source |
|---|---|
| Container | containers/api_container.py |
| Dependencies | containers/dependencies.py |
Repository Pattern
Repositories abstract database operations behind interfaces. See SqlRepository for the base implementation.
| Repository | Source |
|---|---|
| Base | infra/repositories/sql_repository.py |
| User | infra/repositories/user_repository.py |
| Trip | infra/repositories/trip_repository.py |
Service Layer
Services contain business logic and use repositories via UoW. See core/services/ for implementations.
Guidelines
- Inject
BaseUnitOfWorkin the service constructor - Use repository methods from
uowto 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.
| Orchestrator | Purpose |
|---|---|
ChatOrchestrator | AI conversation flow |
FlightSearchOrchestrator | QPX flight search |
AccommodationSearchOrchestrator | Hotel 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.
| Agent | Purpose | Location |
|---|---|---|
| Context Agent | Extract trip requirements | core/agents/context_agent/ |
| Conversational Agent | User dialogue | core/agents/conversational_agent/ |
| Trip Planner Agent | Search execution | core/agents/trip_planner_agent/ |
See AI Orchestration for detailed documentation.
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
- Define entities in
models/database_entities/if needed - Create repository in
infra/repositories/inheriting fromSqlRepository[Entity] - Define service interface in
interfaces/core/services/ - Implement service in
core/services/injectingBaseUnitOfWork - Add router endpoints in
api/v1/routers/with DI - Register router in
api/v1/api.py
Related Documentation
- AI Orchestration — AI agent system and tools
- Unit of Work — Transaction management details
- TTV Scoring — Preference and feature scoring