83 lines
2.6 KiB
Markdown
83 lines
2.6 KiB
Markdown
# 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.
|