The standing default
Hexagonal architecture and feature-based structure, on the backend and the frontend, in any language — applied without asking, departed from only with a stated reason.
Hexagonal architecture (ports and adapters) and feature-based folder structure. Always favour both, on the backend and the frontend, in any language.
This is a default, not a proposal. Apply it without asking. Depart from it only with a stated reason.
The three rules
1 — Organise by feature first, then by direction of dependency.
feature/
core/ the rules. Imports no framework.
inbound/ adapters that call in — HTTP, CLI, queue consumers, UI
outbound/ adapters that call out — persistence, HTTP clients, mail
api/ the explicit public surface other features may importNo controllers/ services/ models/ utils tree, and no layered tree wearing
different names. A folder set that renames the layers without changing which
axis is primary is the same mistake with better vocabulary.
2 — Dependencies point inward. core holds the rules and imports no
framework; adapters import core; features cross boundaries only through an
explicit api surface.
3 — Enforce the dependency rule with a test. ArchUnit, dependency-cruiser, eslint boundaries — whatever the stack offers.
A convention a document states decays. A convention a test states cannot.
Why it is the default here
| It buys | Concretely |
|---|---|
| The second client | The rules survive a different delivery mechanism, a different database, a different framework generation |
| A testable core | The rules are provable without standing up half the product — which is what a tracer bullet story needs |
| A place for the boundary rule | Standards live at the boundary: the plain word in the core, the industry word mapped in the adapter |
| Cheap reversal | A choice replaceable behind an interface you own does not earn a decision record |
Pages
| Page | Covers |
|---|---|
| Hexagonal architecture | The full rules, the rationale, and the traps |
| The operation | The use case contract, the wire/command split, and declaring what an infrastructure failure means |
| Feature structure | The folder layout in a TS/JS repository, and what may cross a feature boundary |
| Micro-frontends | When splitting a frontend earns its cost, and the property that makes the split work |