# Code Style Guide ## Purpose This repository is a Python clean architecture template. Write code that keeps the core portable, adapters thin, and wiring explicit. ## Layer rules - `domain/` contains entities and domain errors only - `usecase/` contains application logic and ports - `repository/` contains repository implementations - `adapter/` contains framework, config, DI, HTTP, and observability integrations - Dependencies must point inward - `domain/` must not import anything from project internals - `usecase/` may import `domain/`, but must not import `adapter/`, `repository/`, FastAPI, or OpenTelemetry - `repository/` and `adapter/` may depend on `usecase/` ports and `domain/` ## Dependency inversion - Define interfaces as `Protocol` types at the point of use - Keep implementations in outer layers - Inject dependencies through constructors - Prefer explicit wiring in `adapter/di/container.py` - Do not hide object creation behind magic globals ## Python conventions - Use simple names - Prefer dataclasses for immutable values, config sections, and request models inside inner layers - Keep `__init__.py` files empty - Do not use `from __future__ import annotations` - Prefer one clear responsibility per class - Keep functions small and direct ## Comments and errors - Add comments only when the code is not obvious - Keep comments short - Do not end comments with a period - Keep error messages short - Do not end error messages with a period ## HTTP rules - Keep FastAPI code inside `adapter/http/fastapi/` - Keep HTTP request and response schemas inside the HTTP adapter - Handlers should translate HTTP input to usecase calls and map domain errors to HTTP responses - Do not move business logic into routers, dependencies, or middleware ## Observability rules - Inner layers know only `Logger`, `Metrics`, and `Tracer` ports - OpenTelemetry code stays in `adapter/otel/` - Use `Noop` implementations when a signal is disabled - Request logging belongs in the HTTP adapter ## Configuration rules - Keep non-secret defaults in `config/app.yaml` - Read secrets from env vars - Merge YAML, `.env`, and process env into one typed config tree - Do not read env vars directly inside `domain/` or `usecase/` ## Example ```py from typing import Protocol class UserRepository(Protocol): def get(self, user_id: str) -> User | None: ... class GetUser: def __init__(self, repository: UserRepository, logger: Logger, tracer: Tracer) -> None: self._repository = repository self._logger = logger self._tracer = tracer ``` The use case owns the contract. The repository implementation lives outside the use case layer.