master/docs/CODESTYLE.md
2026-03-26 21:56:10 +03:00

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.