ADR-001: Feature-first layout, rings inside each feature
Status: accepted
Context
Two layouts are common for Clean Architecture in Python:
- Layer-first:
domain/,application/,infrastructure/,presentation/at the top, each containing a sub-package per feature. - Feature-first: one package per feature, each containing the four rings.
The template's first job is to be replaced: a user must remove the example domain and add their own without hunting through four trees. Its second job is to make the rings visible.
Decision
Feature-first, with the four rings inside every feature and inside shared/:
shared/{domain,application,infrastructure,http}
tournaments/{domain,application,infrastructure,http}
bootstrap/
Cross-feature imports are forbidden; shared never imports a feature. The architecture
tests enforce both, and enforce the ring order inside every package.
Consequences
- Removing or adding a feature touches one directory plus
bootstrap(and marked blocks). - Each feature reads top-to-bottom as a vertical slice, which is how people learn it.
- The ring names are repeated in every feature, so the layer discipline is no less visible than in a layer-first tree; it is simply applied per feature.
shared/is under permanent pressure to grow. The rule: something goes there only when two features need it and it has no feature-specific meaning.
Proof
- proof:dependency-rule - every module imports inward; checked on the real package and
falsified on synthetic packages (
tests/architecture). - proof:feature-isolation - no feature imports another,
sharedimports no feature. - proof:example-removal - the layout's promise that the example is one folder plus marked
blocks:
init_project.py --remove-exampleleaves a working project (tests/template).