Skip to content

Architecture Overview

CMS API follows a layered architecture where each layer has a single responsibility and communicates only with the layer directly below it.

Layers

┌─────────────────────────────────────┐
│              HTTP Request           │
└──────────────────┬──────────────────┘
┌──────────────────▼──────────────────┐
│              Router                 │  app/api/v1/
│  Receives request, returns response │
└──────────────────┬──────────────────┘
┌──────────────────▼──────────────────┐
│              Schema                 │  app/schemas/
│  Validates input, shapes output     │
└──────────────────┬──────────────────┘
┌──────────────────▼──────────────────┐
│              Service                │  app/services/
│  Business logic and decisions       │
└──────────────────┬──────────────────┘
┌──────────────────▼──────────────────┐
│            Repository               │  app/repositories/
│  Database communication only        │
└──────────────────┬──────────────────┘
┌──────────────────▼──────────────────┐
│              Model                  │  app/models/
│  SQLAlchemy table definitions       │
└──────────────────┬──────────────────┘
┌──────────────────▼──────────────────┐
│            PostgreSQL               │
└─────────────────────────────────────┘

Layer Responsibilities

Router

  • Receives HTTP requests
  • Invokes the appropriate service method
  • Returns HTTP responses with correct status codes
  • Has no business logic

Schema (Pydantic)

  • Validates and deserializes incoming request data
  • Serializes outgoing response data
  • Ensures sensitive fields like password_hash never leak in responses

Service

  • Contains all business logic
  • Decides what is allowed, what happens, and in what order
  • Coordinates multiple repository calls within a single transaction
  • Raises appropriate exceptions on business rule violations

Repository

  • The only layer that communicates with the database
  • Executes SQLAlchemy queries
  • Uses flush() instead of commit() — transaction control belongs to the service layer
  • Never contains business logic

Model

  • Defines database tables as Python classes
  • Declares relationships between tables
  • Contains only simple helper properties

Request Flow Example

POST /api/v1/auth/login

1. Router         receives { email, password }
2. LoginRequest   validates schema — correct format?
3. AuthService    is email registered? is password correct? is user active?
4. UserRepository SELECT user WHERE email = ?
5. AuthService    hash comparison, generate tokens
6. TokenRepository INSERT refresh token
7. UserRepository UPDATE last_login
8. LoginResponse  serialize user + tokens
9. Router         return 200 OK

Transaction Management

Repositories use flush() to stage changes without committing. The get_db() dependency in database.py owns the transaction lifecycle:

Request starts  → session opens
All operations  → flush() (staged, not committed)
Request succeeds → commit()
Exception raised → rollback()
Request ends    → session closes

This ensures that multiple repository calls within a single service method are always atomic.