8. Cross-Cutting Concepts

8.1 Authentication & Authorisation

  • JWT RS256 tokens issued by the Users Service on login

  • All services validate tokens locally using the shared public key — no network call required

  • Token payload carries user_id, email, and roles

  • Tokens expire after 15 minutes; refresh tokens (opaque, stored in Users DB) are valid for 30 days

8.2 Error Handling

All services return errors in a uniform envelope:

{
  "error": {
    "code": "RESOURCE_NOT_FOUND",
    "message": "User with id 42 does not exist"
  }
}

FastAPI exception handlers map domain exceptions to HTTP status codes consistently across all services.

8.3 Logging

  • Structured JSON logs (via structlog)

  • Every request logged with trace_id, user_id, method, path, status_code, duration_ms

  • trace_id is propagated via X-Trace-Id HTTP header across service calls

8.4 Observability

Signal

Tooling

Logs

stdout → log aggregator (e.g. Loki)

Metrics

Prometheus /metrics endpoint on each service

Tracing

OpenTelemetry SDK; traces exported to Jaeger/Tempo

8.5 Input Validation

  • All request bodies validated by Pydantic v2 models

  • Validation errors return 422 Unprocessable Entity with field-level detail

  • No raw SQL — all queries go through SQLAlchemy 2 to prevent injection

8.6 Database Migrations

  • Managed by Alembic, one migration environment per bounded context

  • Migrations run automatically on service startup in development; applied manually (CI gate) in production

8.7 CORS

  • Configured on each FastAPI service to allow requests only from the known SPA origin

  • Credentials (cookies / Authorization header) allowed explicitly

8.8 Rate Limiting

  • Applied at the nginx layer: 100 requests/minute per IP for unauthenticated endpoints

  • Authenticated endpoints: 1 000 requests/minute per user_id (via nginx limit_req or an API gateway)