Layered Architecture: The Default, Its Pros and Sinkholes
What This Concept Is
Layered architecture (also called n-tier) is the most common architectural style in existence. It partitions the system into horizontal layers, each with a single technical responsibility, where every request flows top-to-bottom:
+---------------------------------------+
| Presentation (UI, controllers) |
+---------------------------------------+
| Business (services, use cases) |
+---------------------------------------+
| Persistence (repositories, DAO) |
+---------------------------------------+
| Database (tables, indexes) |
+---------------------------------------+
The core rule: each layer only talks to the layer immediately below it. Presentation does not skip business to hit the DB. Business does not know about HTTP.
Layered architecture has two important sub-concepts:
- Layers of isolation. A layer is "closed" if requests must pass through it to reach the next one. Closing layers buys you the ability to change one layer without changing the ones above (swap ORMs, swap databases). An "open" layer can be skipped, which is simpler but removes the isolation.
- The architecture sinkhole anti-pattern. When most requests pass through a layer without that layer doing any real work (the business layer just forwards every call to persistence), you have paid for the layer without getting the benefit. Richards and Ford's 80/20 rule of thumb: if more than 20% of requests are simple pass-throughs, you have a sinkhole.
Why It Matters Here
Layered is the style most teams default into without naming it. Frameworks like Spring, Django, Rails, ASP.NET all encourage it out of the box. That is fine -- if you chose it. It is dangerous when:
- your app is not request-oriented (e.g. data pipelines, event processors)
- your domain is complex enough that "business logic" hiding inside service classes turns into spaghetti
- the sinkhole anti-pattern dominates and every "layer" is just an indirection
Later concepts (modular monolith, microservices) are reactions to the failure modes of layered done badly. You must be able to draw a clean layered design and name its sinkholes before you can critique any other style.
Concrete Example
A CRUD web app for Order in a layered architecture:
HTTP POST /orders
|
v
[OrderController] (presentation)
| validates request, extracts DTO
v
[OrderService] (business)
| applies discount, checks inventory policy, emits domain event
v
[OrderRepository] (persistence)
| maps domain to row, calls ORM
v
[orders table] (database)
OrderController knows about HTTP. OrderService does not. OrderRepository knows about SQL. OrderService does not. That isolation means we can swap Postgres for MySQL without touching OrderService.
Compare to a sinkhole version:
[OrderController.getOrder]
-> OrderService.getOrder(id)
-> OrderRepository.findById(id) <- pass-through
-> return row
OrderService.getOrder does nothing except forward. If 80% of your endpoints look like this, the business layer is a sinkhole. The solution is not to delete the layer (you still need the isolation for the 20% that do real work); it is to be honest in ADRs and code review about when a layer exists only for consistency.
Common Confusion / Misconception
"Clean Architecture / Hexagonal / Onion is a different style from layered." They are refinements of layered thinking, not replacements. They add the dependency-rule constraint (inner layers know nothing about outer ones) and pull policy out of the framework. The topology is still layers. M01's hexagonal treatment is a specialization of this concept.
"Every system should be layered; it is the safe default." Layered is a good default for classic request/response CRUD web apps with moderate domain complexity. It is a bad default for data transformations (pipeline is better), high-throughput async workflows (event-driven is better), or systems with strong independent deploy needs (modular monolith or microservices).
"Closed layers mean the DB can't be touched from the top." Closed means the call path cannot skip a layer in normal flow. It does not mean infrastructure boundaries are enforced. Tools like ArchUnit and Packwerk (see Concept 6) are how you turn closed layers into enforced closed layers.
How To Use It
Before you accept "layered" as the style for a new system, answer:
- Is this request-oriented? (A request comes in, a response goes out, maybe with side effects.) If not, consider pipeline or event-driven instead.
- Do layers actually do work? Sketch one typical request and tag each layer as
policy,translation, orpass-through. If a majority are pass-through, you are buying a sinkhole. - Are the layers closed? If an operations dashboard is hitting the DB directly, the isolation story is already broken and "layered architecture" is decorative.
For an existing system, run a one-hour audit:
Check Yourself
- What is the difference between a closed layer and an open layer? When would you deliberately open one, and what do you give up?
- State the sinkhole anti-pattern in your own words. Why does the 80/20 rule of thumb exist?
- Richards and Ford rate layered architecture low on scalability and elasticity. Why? Give the root cause, not just "because monolith."
Mini Drill or Application
Pick a web framework you know (Spring, Django, Rails, ASP.NET, Express). In 10 minutes, write:
- The four layers its conventions push you toward, with one class/file name per layer.
- One concrete endpoint you have written that was a sinkhole. Name what it did in each layer.
- One concrete endpoint where the layered structure paid for itself (because one layer absorbed a real change when swapping a dependency).