All Python templates

Python Clean Architecture

Layered architecture with use-cases, repositories, and dependency inversion.

DevZone Tools1,450 copiesUpdated Mar 4, 2026Python
# 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