The structure
Layered architecture partitions a system horizontally by technical responsibility. Presentation handles input and output, business holds the rules, persistence translates between objects and storage, and the database stores state. Each layer is a logical grouping — they usually all run in the same process.
Closed layers and layer isolation
The property that makes layering worth anything is layer isolation: a change in one layer must not force changes in others. That requires layers to be closed — a request must pass through every layer, and presentation may never call persistence directly.
This feels wasteful, and that is the trade. Closed layers mean you can replace the whole persistence layer without touching business logic, and swap REST for gRPC without touching anything below presentation. Open a layer "just for performance" and you have traded the only benefit you were paying for.
A request through the layers
POST /orders
presentation: OrderController.create(req)
-> validate shape, authn/z, map JSON -> CreateOrderCommand
business: PlaceOrder.execute(command)
-> check stock rules, pricing, credit limit
-> build Order aggregate
persistence: OrderRepository.save(order)
-> map aggregate -> rows, one transaction
database: INSERT INTO orders ...; INSERT INTO order_lines ...
response bubbles back up, mapped to an OrderResponse DTONote the mapping at each boundary. Passing the database row all the way up to the HTTP response is the single most common way layered systems become impossible to change: the table schema becomes the public API.
What it's genuinely good at
- Onboarding. Any engineer can find anything in minutes. That is a real, underrated architectural quality.
- Separation of technical concerns. Persistence changes (new ORM, new database) stay contained.
- Testability by layer. Business logic can be tested with fake repositories.
- Cost. Zero infrastructure, zero distribution, works inside a monolith or inside a single microservice.
The sinkhole anti-pattern
A sinkhole request is one that passes through every layer while each layer does nothing but forward it — typically simple reads. If 80% of your requests are sinkholes, the layering is pure overhead.
The fix is not to abolish layering; it is to allow a documented read path that bypasses the business layer for pure queries (effectively CQRS-lite), while all writes stay closed. Aim for roughly 20% sinkholes; beyond that, question the style.
Leaky layers
| Leak | Symptom | Fix |
|---|---|---|
| ORM entities in controllers | Schema change breaks the API contract | DTOs at the presentation boundary |
| SQL in business logic | Cannot change storage without touching rules | Repository interfaces |
| HTTP types in business layer | Domain code imports the web framework | Commands and plain results |
| Business rules in the database | Logic split across triggers and code | Keep invariants in one place |
| Persistence calling business | Cycles, unclear ownership | Events or an application service above both |
Layered vs hexagonal vs vertical slices
Layering partitions by technology. Its structural weakness is that a single feature cuts across all layers, so feature work is never local.
- Hexagonal keeps a similar dependency discipline but inverts it: the domain defines the interfaces and infrastructure implements them, so dependencies point inward rather than downward.
- Vertical slice organises by feature first (
orders/place-order/containing its handler, rules and query), keeping change local at the cost of some repetition.
These combine well: vertical slices on the outside, layer discipline inside a slice, hexagonal ports where infrastructure is touched.
Rules that keep it healthy
- Layers are closed. Skipping is a reviewed exception, not a habit.
- Every boundary maps types. No entity escapes persistence; no DTO enters the domain.
- Dependencies point one direction only — enforce it with an import linter.
- Business logic never imports the web or ORM framework.
- Measure your sinkhole ratio; a high one means the style is mismatched to your workload.
Interview framing
Strong answer
"Layered inside each service, closed layers, DTO mapping at both boundaries. It gives us the fastest onboarding and contains storage changes. Its weakness is that a feature touches four layers, so for the read-heavy dashboard I'd add a dedicated query path that skips the business layer and hits read models directly, while all writes remain fully layered."
Traps
- Calling layered architecture "n-tier" and implying physical separation — tiers are deployment, layers are logical.
- Claiming layering gives domain modularity. It gives technical modularity only.