Layering & dependencies
Layering is not diagram theater—it constrains dependency direction: inner layers must not know outer ones; domain logic must not depend on framework details. The structure below works in monoliths, modular monoliths, and multi-service setups.
Four logical layers
| Layer | Responsibility | May depend on |
|---|---|---|
| Presentation | HTTP/UI/CLI, serialization, auth entry | Application interfaces |
| Application | Use-case orchestration, transactions, DTO mapping | Domain |
| Domain | Entities, value objects, invariants | No infrastructure |
| Infrastructure | DB, queues, third-party SDKs | Implements ports defined inward |
Rule: dependencies point inward (presentation → application → domain; infrastructure implements ports, domain does not import adapters).
[ UI / API ] → [ Use cases ] → [ Domain ]
↑
[ Adapters: DB, MQ, HTTP client ]
Mapping to Clean / Hexagonal
| Term | Layer here |
|---|---|
| Entity / domain model | Domain |
| Use case / interactor | Application |
| Controller / presenter | Presentation |
| Gateway / repository impl | Infrastructure adapter |
Define ports on the inner side; implement adapters outside. Swapping DB or UI framework should touch adapters, not business rules.
Monolith vs modular monolith vs services
| Shape | When | Layering note |
|---|---|---|
| Monolith | Small team, exploration, strong local transactions | Enforce deps with packages/targets (ArchUnit, SwiftPM) |
| Modular monolith | Medium scale, separate compile/test | Tiny public api per module |
| Services | Independent scale/deploy | Thinner layers; contracts replace in-process calls |
tip
Default: modular monolith + sharp boundaries; split services on deploy cadence, scaling, or failure isolation—not on org chart alone.
Violations and fixes
| Violation | Effect | Fix |
|---|---|---|
| Domain imports ORM annotations | Storage change ripples everywhere | Repository port + infra mapping |
| Controller owns rules | Duplication, hard tests | Application/domain services |
| Cross-layer DTO leak | API churn breaks internals | Map at boundaries |
| Shared “util” imported everywhere | Hidden global coupling | Ports or single adapter |
Testing by layer
| Layer | Tests | Goal |
|---|---|---|
| Domain | Pure unit | Invariants, state machines |
| Application | Use-case with fakes | Orchestration, transactions |
| Infrastructure | Integration | Real SQL/broker (containers OK) |
| Presentation | Contract / sampled E2E | HTTP schema, critical paths |