Layered#
The traditional Presentation / Business / Data layering. Each layer depends only on the one below. Sometimes called “n-tier” when distributed across machines.
The Classic Three Layers#
flowchart TB
subgraph Presentation
UI[UI / HTTP Handlers / View Models]
end
subgraph Business
Svc[Domain Logic / Services]
end
subgraph Data
Repo[Repositories / ORM]
DB[(Database)]
end
UI --> Svc
Svc --> Repo
Repo --> DB
The standard split that most enterprise apps still use. The rule is dependencies only point downward; presentation can call business, business can call data, but never the reverse. Calls go top-down only.
Presentation calls Business.
Business calls Data.
Lower layers don’t know about higher ones.
N-Tier#
The same layering, but each tier runs on a different machine. The deployment structure that defined late-90s and 2000s enterprise architecture, and still appears in most managed-cloud reference architectures and Java EE / .NET shops.
Web tier, HTTP servers behind a load balancer.
Application tier, business services.
Database tier, one or more database servers.
Common in late-90s / 2000s enterprise. Still appears in Java EE / .NET shops and most managed-cloud reference architectures.
MVC / MVVM / MVP#
Variations of layered for UI-heavy applications. They split the Presentation layer into more pieces, each with a different opinion about who knows what. The differences are subtle in practice; pick the one your framework uses and stop worrying.
MVC (Model-View-Controller), controller mediates between view and model.
MVP (Model-View-Presenter), presenter handles UI logic; view is passive.
MVVM (Model-View-ViewModel), view binds to a viewmodel; popular in WPF / SwiftUI / Vue / Knockout.
The differences are mostly about who knows what.
Strengths#
What layered does well, especially for teams without specialized domain logic. Familiarity, parallel development, and clear responsibilities are the genuine wins; enough that most enterprise codebases still ship with this structure and don’t suffer for it.
Simple and familiar, the default mental model for a generation of enterprise developers.
Parallel development, frontend and database teams can work somewhat independently behind the layer interfaces.
Clear responsibilities, each layer has a job.
Weaknesses#
What layered struggles with as the codebase ages. Anemic domain, skip-layer leaks, single-axis splitting that puts all related code far apart, and weak handling of cross-cutting concerns are the failure modes that show up in every old enterprise codebase.
Anemic domain, the Business layer becomes a thin shell of “service classes” calling repositories; entities are data bags. Behavior leaks into services that orchestrate everything.
Skip-layer leaks, in practice, controllers reach directly into data when “convenient”; the layering becomes documentation rather than enforcement.
Single dimension, everything is split by layer, not by feature. A single change touches every layer; modifying the data model often ripples through three files.
No good story for cross-cutting concerns, caching, transactions, authorization either pollute every layer or sit awkwardly outside.
When Layered Is Fine#
The use cases where the simple three-layer structure is the right answer. Small apps, CRUD, and teams that don’t have the headcount or appetite for DDD-style domain modeling all fit cleanly; layering is a perfectly good solution to a perfectly mundane problem.
Small, simple apps with limited domain logic.
Teams that aren’t doing DDD and don’t need the indirection of Hexagonal / Clean.
CRUD apps where the database schema is the domain model.
A modular monolith with thin layers and clear feature boundaries can work well for years.
When to Move Beyond#
The four signs that layered has aged out for your specific codebase. Each one is observable from inside the team; service-class bloat, duplicated cross-cutting concerns, database-bound tests, and “skip layers because it’s faster” becoming a regular phrase.
The Business layer fills with “Service” classes that mostly do orchestration.
Cross-cutting concerns are duplicated across layers.
Tests require the database to run.
You hear “skip layers because it’s faster” often.
Common destinations.
Hexagonal for serious domain logic.
Vertical-slice / feature-based organization for products that change by feature, not by technical layer.
Vertical Slice (Feature-Based) Architecture#
A modern reaction to traditional layering. Where layered splits by technical concern, vertical-slice splits by business feature; a feature owns its controller, service, repository, validators, and view models in one place. Different features can use different patterns.
Organize by feature, not by layer.
Each feature contains its own controller, service, repository, validators, view models.
Different features can use different patterns, one feature is CRUD, another is event-sourced, a third is hexagonal.
Strengths.
Co-located code is easier to change.
Different features evolve at different rates without dragging everything else along.
Lower risk of skip-layer leaks; the boundaries are inside the feature.
Weaknesses.
Repetition between features unless shared abstractions are extracted carefully.
Less consistent structure across the codebase.
Often a great fit for modular monoliths.
Layered + DI#
Most layered codebases use dependency injection to thread layers. The composition root wires real implementations at startup; the rest of the code only sees the interfaces it declares it needs. Tests substitute fakes without touching real infrastructure.
Constructors take their dependencies as parameters.
A composition root (entry point) wires real implementations.
Tests substitute fakes.
DI containers (Spring, .NET, Dagger, NestJS, Inversify) are common but not required; manual wiring is often clearer in smaller systems.
The Realistic Take#
Most enterprise apps in production today are layered. The pattern works well enough that most teams haven’t moved off it, and the bigger problem isn’t choosing a fancier architecture; it’s actually enforcing the layering that’s already there. Layered done with discipline beats Hexagonal done sloppily.
Use static analysis to forbid cross-layer leaks.
Test the business layer without the database.
Co-locate code that changes together.
Don’t let “Service” classes become 1000-line god objects.
Layered done with discipline beats Hexagonal done sloppily.