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 fromdomain/only. Abstract service interfaces (publisher, logger, monitor) are defined here, not in infrastructure.infrastructure/implements interfaces defined indomain/andapplication/. It may import fromconfig/for settings.api/is the only layer that imports from all others (viadependencies.py). Routes import handlers fromapplication/, not frominfrastructure/directly.- All adapter selection logic lives in
infrastructure/*/factories.py.api/dependencies.pycalls these factories — it does not construct adapters directly.