Documentation

Directory Structure

The canonical folder layout for a new service — how the four layers map to real files and directories.

Show:

Framework implementations

Every FastAPI service follows this structure. New services should be scaffolded to this layout from the start. Do not deviate without a documented reason.

<service-name>/
├── api/                              # Driving adapter — FastAPI app, routes, middleware
│   ├── main.py                       # App factory: creates FastAPI instance, registers middleware & routes
│   ├── lifespan.py                   # Startup/shutdown: DB pools, background tasks, resource cleanup
│   ├── dependencies.py               # DI container: all Depends() factories live here
│   ├── exceptions.py                 # Domain-specific exception classes surfaced at API layer
│   ├── responses.py                  # Shared response DTOs and envelope helpers
│   ├── security.py                   # Auth helpers, action/resource constants, require_* dependencies
│   ├── routes/                       # Route handlers, grouped by feature/domain
│   │   ├── <feature>.py              # e.g. accounts.py, messages.py
│   │   ├── admin/                    # Admin-only routes (separate sub-router)
│   │   ├── auth/                     # Auth endpoints (login, signup, oauth, otp)
│   │   ├── health.py                 # Health check endpoint
│   │   └── webhooks/                 # Inbound webhook handlers
│   ├── schemas/                      # Pydantic request/response models
│   │   ├── <feature>_schemas.py      # e.g. account_schemas.py
│   │   ├── query_params.py           # Shared query parameter models
│   │   └── __init__.py
│   └── middleware/                   # ASGI middleware (order matters — see middleware guide)
│       ├── __init__.py               # register_middleware(app) — single registration point
│       ├── cors.py                   # CORS (registered first, executes last)
│       ├── request_context.py        # Request ID, path, method, actor context
│       ├── logging.py                # Structured request/response logging
│       ├── jwt_auth.py               # JWT validation, actor context population
│       └── error_handler.py          # Exception → JSON response mapping
│
├── application/                      # Use cases, business logic, abstract interfaces
│   ├── unit_of_work.py               # Abstract UoW interface
│   ├── <feature>/                    # One folder per domain feature/aggregate
│   │   ├── create.py                 # Command handler: CreateXxxHandler
│   │   ├── update_<aspect>.py        # Command handler: UpdateXxxHandler
│   │   ├── delete.py                 # Command handler: DeleteXxxHandler
│   │   └── query_handlers.py         # Read-side handlers (if needed)
│   ├── authentication/               # Auth handlers and abstract services
│   │   ├── commands.py               # Auth command definitions
│   │   ├── handlers.py               # Login, signup, token refresh, OTP handlers
│   │   ├── jwt_service.py            # Abstract JWT service interface
│   │   ├── password_service.py       # Abstract password hashing interface
│   │   └── email_service.py          # Abstract email service interface
│   ├── events/
│   │   └── publisher.py              # Abstract EventPublisher interface
│   ├── logging/
│   │   └── service.py                # Abstract LoggingService interface
│   └── monitoring/
│       └── performance.py            # Abstract PerformanceMonitor interface
│
├── domain/                           # Pure domain — no external dependencies
│   ├── <feature>/
│   │   ├── aggregate.py              # Aggregate root(s)
│   │   ├── value_objects.py          # Value objects (immutable, equality by value)
│   │   └── repository.py            # Abstract repository interface
│   └── __init__.py
│
├── infrastructure/                   # Concrete adapter implementations
│   ├── authentication/               # Auth provider adapters
│   │   ├── jwt_token.py              # PyJWT implementation
│   │   ├── password_bcrypt.py        # bcrypt hashing
│   │   ├── oauth_<provider>.py       # OAuth adapters (google, facebook, etc.)
│   │   ├── otp_generator.py          # OTP generation
│   │   └── factories.py             # Auth service builders
│   ├── cache/
│   │   ├── adapters/
│   │   │   ├── inmemory.py
│   │   │   └── redis.py
│   │   └── factories.py
│   ├── database/
│   │   ├── factories.py              # Adapter registry and session factory builder
│   │   ├── session_factory.py        # SQLAlchemy async session factory
│   │   ├── unit_of_work.py           # UoW factory/registry
│   │   ├── migrations/               # Alembic migration scripts
│   │   └── adapters/
│   │       ├── sqlalchemy/           # SQLAlchemy (production)
│   │       │   ├── models/           # ORM model definitions
│   │       │   ├── mappers/          # Domain ↔ ORM bidirectional mappers
│   │       │   ├── repositories/     # Concrete repository implementations
│   │       │   ├── session.py        # Async session configuration
│   │       │   └── unit_of_work.py   # SQLAlchemy UoW implementation
│   │       └── inmemory/             # In-memory (testing/development)
│   │           ├── repositories/
│   │           └── unit_of_work.py
│   ├── events/
│   │   ├── adapters/
│   │   │   ├── inmemory.py
│   │   │   ├── sns.py
│   │   │   └── celery.py
│   │   └── factories.py
│   ├── logging/
│   │   ├── adapters/
│   │   │   └── structured.py
│   │   ├── config.py                 # JSON formatter, root logger setup
│   │   └── factories.py
│   ├── monitoring/
│   │   ├── adapters/
│   │   │   ├── inmemory.py
│   │   │   └── cloudwatch.py
│   │   └── factories.py
│   └── storage/
│       ├── adapters/
│       │   ├── local_file_adapter.py
│       │   └── s3_adapter.py
│       └── factories.py
│
├── config/
│   └── settings.py                   # Pydantic BaseSettings — all env vars, adapter flags
│
├── tests/
│   ├── conftest.py                   # Shared fixtures (in-memory adapters, test app)
│   ├── test_routes/                  # Route-level integration tests
│   ├── test_handlers/                # Application handler unit tests
│   └── test_repositories/           # Repository tests (in-memory + SQLAlchemy)
│
├── scripts/                          # CLI/admin scripts
├── storage/                          # Runtime storage (logs, local media, encryption keys)
├── pyproject.toml                    # Dependencies, tool config (black, ruff, mypy, pytest)
├── Dockerfile                        # Multi-stage build
└── docker-compose.yml               # Local dev stack (app + postgres + redis)

Rules

  • domain/ has zero imports from any other layer. If a domain file needs something, it defines an abstract interface that infrastructure implements.
  • application/ imports from domain/ only. Abstract service interfaces (publisher, logger, monitor) are defined here, not in infrastructure.
  • infrastructure/ implements interfaces defined in domain/ and application/. It may import from config/ for settings.
  • api/ is the only layer that imports from all others (via dependencies.py). Routes import handlers from application/, not from infrastructure/ directly.
  • All adapter selection logic lives in infrastructure/*/factories.py. api/dependencies.py calls these factories — it does not construct adapters directly.