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.