الدرس 1 من 2
What a Layer Actually Buys
Layers are a dependency rule, not a folder convention: everything above may depend on what is below, and nothing below may know what is above.
Layered architecture organises a system into horizontal tiers — typically presentation, application, domain and persistence — with a rule about the direction dependencies may point. It is the most common application structure in the world, and most teams using it have never made a decision about it; it arrived with the framework. That is worth fixing, because the pattern has genuine strengths and two specific failure modes, and you cannot get the first while avoiding the second by accident.
The layers and the dependency rule
Dependencies point downward only. The arrow that must never exist is the one from persistence back up to presentation.
Four stacked layers. Presentation depends on application, application depends on domain, domain is depended upon by persistence which implements its interfaces. Requests enter at the top and travel down; no layer imports anything from a layer above it.
Presentation
Pages and components
HTTP, rendering, forms
Application
Use cases
orchestration, transactions
Domain
Entities and rules
the part with no imports
Persistence
Repositories
implements domain interfaces
What that rule buys is substitutability in one direction and comprehension in both. Because persistence knows nothing about HTTP, the same domain can serve a web page, a scheduled job and a command-line tool. Because presentation knows nothing about SQL, a schema change does not reach the templates. And because the direction is fixed, a new engineer can predict where any given piece of code lives, which is a real and underrated benefit on a team that hires.
Closed and open layers
A *closed* layer must be passed through: presentation may not skip the application layer and call persistence directly. An *open* layer may be bypassed. Most discussions assume all layers are closed without saying so, and most real systems quietly open one — usually for read-only queries that would otherwise cross three layers to fetch a list.
Closed layers versus a deliberately opened read path
All layers closed
Every request traverses every layer. A read goes presentation to application to domain to persistence and back.
- One consistent path, so behaviour is predictable
- Cross-cutting rules applied in the application layer cannot be skipped
- Isolation is genuine: changing one layer cannot surprise a distant one
- Simple reads carry the full ceremony of a write
- Four representations of the same data and the mappers between them
- Pressure to bypass builds, and eventually somebody does it quietly
Choose when: Reads and writes are of similar complexity, or the cross-cutting rules in the application layer must apply to reads too — authorisation filtering, for example.
Closed writes, open reads
Writes traverse every layer. Read-only queries are allowed to go from presentation to a dedicated query component that returns exactly what a screen needs.
- Removes most of the mapping ceremony where it buys least
- Query shapes can be tuned per screen without distorting the domain
- The bypass is explicit and reviewable rather than smuggled in
- Two paths to learn instead of one
- Authorisation must be applied on the query path deliberately
- Easy to let the read path grow into a second, undisciplined application layer
Choose when: Read shapes differ substantially from the domain model, and the resulting mapping cost is measurable rather than aesthetic. This is the small step toward CQRS covered later in this chapter.
In practice
Deciding whether the sinkhole mattered
An internal HR system had a strict four-layer structure. A new technical lead measured it: of 140 endpoints, 118 did nothing in the application and domain layers beyond forwarding. The team proposed collapsing to two layers.
Constraints
- Six developers, one team, single deployable
- The 22 endpoints with real logic include payroll and leave accrual
- Payroll rules change quarterly and are audited
- No appetite for a large refactor
Decision
Keep all four layers for the 22 endpoints that carry business rules. Allow the remaining 118 read-and-list endpoints to use a query component that goes directly to the database and returns screen-shaped results.
Why
The layers were paying for themselves precisely where the rules were — payroll logic is testable without a database and is audited, which is worth ceremony. Applying the same ceremony to "list departments" bought nothing and cost four files per endpoint. The split follows the value rather than a uniform rule.
What it cost
The codebase now has two shapes, and a new joiner must learn when each applies — a genuine cost that the team mitigated with one written rule: "if it writes, or if a rule decides the answer, it goes through the layers". They also had to apply row-level authorisation explicitly on the query path, which had previously been inherited from the application layer, and they added a test that fails if a query component is used without an authorisation filter.
Your persistence layer defines a `Customer` class, and your presentation layer imports it directly to render a profile page. Is the layering violated?RevealHide
The dependency direction is technically legal — presentation is above persistence — but the arrangement defeats the purpose. Your rendering code is now coupled to the shape of a database table, which means a column rename becomes a user-interface change and the domain layer in the middle is bypassed for the thing it exists to protect. The layer rule is necessary and not sufficient: what makes layering valuable is that each layer owns its own vocabulary, and importing a persistence type into a template hands the schema straight through. Note also that this is a case where the honest answer might be to collapse layers rather than add a mapper: if the page is a plain read of one table and no rule applies, the ceremony may not be buying anything.
Key takeaways
- Layering is one rule: depend downward, never upward. Everything else is naming.
- It buys substitutability of the outer layers and predictable code placement, which matters most on teams that grow.
- Closed layers must be traversed; opening the read path is a common and defensible deviation if it is explicit.
- The sinkhole is only a problem when nearly every request passes through without work — measure before judging.
- Multiple representations are worth paying for only where each protects a boundary that changes independently.