N-tier layered architecture
Layered architecture divides code into horizontal tiers — presentation, application, domain, infrastructure — each depending only on the layer below. It buys replaceability and testability but creates a sinkhole when layers merely pass data through without adding value.
The B2B order platform started as a single file — a request handler that read from the database, ran some validation, and returned a response. Within six months the file was 3,000 lines. The team split it into “layers”: a controller package, a service package, a repository package. The layering made the code navigable. Tests could be written against services without a live database. When the team switched from a REST-based database client to a native driver, only the repository package changed. Two years later, the platform had twelve service classes. Each service method followed an identical pattern: validate the input, call the repository, return the result. None of the service methods did anything except delegate — they called one repository method and returned whatever it returned, unchanged. Every change required touching the controller, the service, and the repository in lockstep. The layering that was supposed to provide replaceability had become a ceremony: three files changed for every one-line business rule. The team had the layers right but had never asked what each layer was supposed to add.
The four standard layers
The canonical n-tier model names four layers, each with a specific responsibility:
Presentation layer — handles all user-facing concerns: HTTP request parsing, input deserialization, response formatting, authentication tokens, content negotiation. It knows about the web protocol. It does not know about business rules.
Application layer — orchestrates use cases. It sequences calls to the domain and infrastructure, manages transactions, enforces authorization policy, and publishes events. It knows about the flow of a use case. It does not contain business logic — it delegates to the domain.
Domain layer — contains the business model: entities, value objects, domain services, and the rules that govern them. In the B2B order platform, “an order cannot be confirmed if it has unpaid invoices” is domain logic. This layer has no knowledge of the database, the HTTP framework, or the queue system.
Infrastructure layer — implements the ports that the domain and application layers define: database repositories, email senders, message queue publishers, external API clients. It knows about technology. It does not know about business rules.
Strict versus relaxed layering
Strict layering requires that each layer depends only on the layer immediately below it, and on nothing else. Presentation calls Application; Application calls Domain; Domain defines interfaces that Infrastructure implements. Domain has zero knowledge of Infrastructure — it does not import the database driver, the ORM, or any framework class.
Strict layering gives you the strongest isolation: the domain can be tested with no database, no queue, no external service. It can be understood without knowledge of any technology. It is the foundation on which hexagonal and clean architecture are built (those patterns are strict layering taken to its logical conclusion, which units 04 and 05 cover).
Relaxed layering (also called “open layer” architecture) allows a layer to call any layer below it, not just the immediately adjacent one. Presentation may call Domain directly, bypassing Application. Application may import Infrastructure directly rather than through an interface.
Relaxed layering is pragmatic: for simple CRUD operations the application layer adds no value, and forcing a call through it is ceremony. The tradeoff is that relaxed layering is easier to violate without noticing — once you allow skipping layers for convenience, the discipline erodes and you end up with presentation importing repository classes directly.
▸Why this works
Why does layering exist at all? The original force it addresses is replaceability. When the B2B platform switched database drivers, only the infrastructure layer changed — because no business logic had leaked into it. When the team needed to test order confirmation rules in isolation, they could — because the domain layer had no dependency on the database. Both benefits require that the layer boundaries hold: that no higher layer imports a lower layer’s implementation details. The moment business logic drifts into a repository, or presentation logic drifts into a domain entity, those benefits disappear.
What layering actually buys — and its cost
The concrete benefits of a well-maintained layered architecture:
Replaceability — the infrastructure can be swapped (new database, new message broker, new email provider) without touching the domain. This works only when the domain layer defines the interface and infrastructure implements it.
Independent testability — the domain can be tested with pure in-memory doubles. The application layer can be tested with fake infrastructure. No end-to-end test needed to verify a business rule.
Navigability — engineers know where to look for each kind of change. Changing a URL route: Presentation. Changing the flow of a use case: Application. Changing a business rule: Domain. Changing the SQL: Infrastructure.
The cost: every cross-cutting change (adding a new field to an entity, exposing it in the API, and persisting it) requires touching four files in four packages. The layered architecture trades horizontal modularity for vertical rigidity. Adding a feature is a vertical cut through all layers; the architecture makes that cut explicit and therefore somewhat tedious.
The architecture sinkhole anti-pattern
The architecture sinkhole is the failure mode described in the opening story: a layer that adds no value and simply passes calls through to the layer below.
// Service that is a pure sinkhole — adds nothing
class OrderService {
async getOrder(id: string) {
return this.orderRepository.findById(id); // zero transformation, zero logic
}
async createOrder(dto: CreateOrderDto) {
return this.orderRepository.save(dto); // zero validation, zero domain logic
}
}A sinkhole service is not just useless — it is harmful. It adds indirection without adding value. It forces every change to touch an extra file. It trains the team to add methods to the service as a reflex, even when the service has nothing to contribute.
The sinkhole is not a sign that layering was wrong — it is a sign that the domain layer was hollowed out. If the domain layer contains no real logic, the application layer has nothing to orchestrate, and the service layer becomes a pass-through. The fix is not to remove the layers — it is to push the logic where it belongs: into the domain (units 03-02 covers this in depth as the anemic-domain-model pattern).
▸lesson.inset.note
Mark Richards and Neal Ford recommend the “80/20 rule” for sinkholes: if more than 20% of the layer’s requests simply pass through with no transformation, the architecture is accumulating sinkhole debt. This is not a precise metric but a diagnostic — if you find yourself writing empty delegation methods routinely, the layer structure is not paying for itself.
In the B2B order platform, a new rule is added: orders worth more than $10,000 must be approved by a senior account manager before confirmation. Where does this logic belong in a four-layer architecture, and why?
The team uses strict layering. An engineer proposes that the domain layer should import a logging library directly to record when invariant violations occur. What does this violate and what is the correct approach?
A team has a 50-method OrderService where 40 of the methods simply call the corresponding OrderRepository method and return the result unchanged. What architectural problem does this describe, and what is the root cause?
- 01What are the four standard layers in n-tier architecture and what is each layer's specific responsibility?
- 02What is the difference between strict and relaxed layering, and what is the tradeoff?
- 03What is the architecture sinkhole anti-pattern and what is its root cause?
N-tier layered architecture is the default starting structure for most applications because it directly addresses the most common force: the need to change infrastructure (switch databases, change frameworks, add a new delivery channel) without touching business logic, and the need to test business logic without a real database.
The four layers — presentation, application, domain, infrastructure — each own a distinct concern. The dependency rule inside a layered architecture says every layer depends only downward: presentation calls application, application calls domain, domain defines interfaces that infrastructure implements. No layer imports from a layer above it.
Strict layering enforces that each layer depends only on the layer immediately below. Relaxed layering permits skipping layers for pragmatic cases. Both are valid; strict layering gives stronger isolation at the cost of more discipline.
The architecture sinkhole is the failure mode: a layer that passes calls through without adding value. Sinkholes emerge when the domain layer is empty — when business rules have drifted into the repository or simply never been captured in domain objects. The fix is not to remove the layers; it is to restore the domain layer’s responsibility. The next lesson examines exactly what that means: the difference between an anemic domain model (data bags + service logic) and a rich domain model (behavior with the data it guards).
Practice
Start at the top. Tasks go easiest → hardest: recall a fact, apply it to a case, then a senior-level stretch. Open one, attempt it, then reveal.
Something unclear?
Ask a question about this lesson. Questions are anonymous and go straight to the author to make the lesson better.
Apply this
Put this lesson to work on a real build.