Python Clean Architecture
Layered architecture with use-cases, repositories, and dependency inversion.
# CLAUDE.md — Python Clean Architecture
## Layers (innermost → outermost)
1. **Domain** — entities, value objects, domain events. Pure Python, no I/O.
2. **Use cases** (application services) — orchestrate domain objects. Depend on interfaces (ports), not implementations.
3. **Interface adapters** — repositories, gateways, controllers. Implement the ports.
4. **Frameworks & drivers** — FastAPI, SQLAlchemy, Celery. The thinnest layer.
The dependency rule: outer layers depend on inner. Never the reverse.
## Folder layout
```
src/your_app/
domain/
entities.py # @dataclass(frozen=True, slots=True)
value_objects.py
events.py
use_cases/
create_invoice.py # one file per use case
adapters/
repositories/
http/
queue/
infrastructure/
db/
config.py
```
## Domain
- Entities are dataclasses. Behavior lives on the entity (methods), not in services.
- Value objects are `frozen=True`. They have equality by value, not identity.
- Domain events are dataclasses. Use cases emit them; adapters dispatch them.
- No imports from outer layers. The domain doesn't know SQLAlchemy exists.
## Use cases
- One class or callable per use case. The name is a verb in the imperative: `CreateInvoice`, `ApprovePayment`.
- Constructor takes injected dependencies as `Protocol` types — never concrete implementations.
- The `__call__` (or `execute`) method takes a request DTO and returns a result DTO. Everything serializable.
- No I/O directly inside use cases. They orchestrate; adapters do the work.
## Repositories
- Interface (`Protocol`) lives in `domain/` or `use_cases/`. Implementation lives in `adapters/repositories/`.
- Method names match domain language: `find_by_email`, not `select_user_by_email`.
- Return domain entities, never ORM rows. Map at the boundary.
## Dependency injection
- Wire dependencies in `infrastructure/composition_root.py` (or `main.py`). Use cases never construct their dependencies.
- `dependency-injector` or just plain factory functions both work. Don't reinvent a DI container.
## Testing
- Unit-test use cases with fake repositories — no DB, no network.
- Integration-test repositories against a real database (Docker container, ephemeral).
- End-to-end tests hit the HTTP layer. Keep them few and fast.
## Don't
- Don't import SQLAlchemy or FastAPI from `domain/` or `use_cases/`.
- Don't pass HTTP request objects into use cases. Map to a DTO at the controller layer.
- Don't share database sessions across use cases. Each use case opens and commits its own unit of work.
- Don't reach for clean architecture on a 200-line script. It's overkill below a certain scale.
Other Python templates
Modern Python Rules
Type hints, ruff, black, uv, and pytest — opinionated Python defaults.
Python Data Science (pandas + numpy)
Notebook discipline, vectorization, and reproducible analysis with pandas.
Python asyncio Patterns
asyncio fundamentals: tasks, gather, cancellation, and structured concurrency.
Python CLI Tools (Typer)
Build polished CLIs with Typer, Rich output, and clean argument parsing.
Django + DRF Rules
Django REST Framework conventions: viewsets, serializers, permissions.