Architectural Styles Deep Dive · 6 of 8

Layered / N-tier — Stacks of Responsibility

Layered architecture splits a system into horizontal layers — usually Presentation, Application, Domain, Data — where each layer only depends on the one below. It's the classic enterprise layout you'll meet in nearly every Java EE, Spring, ASP.NET, or Django app. Easy to understand, easy to teach, and surprisingly easy to misuse.

TiersTop-Down DependencyEnterpriseClassic
← Back to Architecture
Quick Facts

The Standard Layers

Basic Concepts

  • Presentation: controllers, view templates, request/response shaping. Knows the protocol (HTTP, gRPC).
  • Application / Service: orchestrates a use case — calls the domain, manages transactions, returns DTOs.
  • Domain: business entities and rules. Should be the part with the most behavior.
  • Data Access / Persistence: repositories, ORM mappings, raw SQL. Talks to the DB.
  • The dependency rule: each layer depends only on layers below it. Presentation calls Application, never the reverse.
  • "N-tier" historically referred to physical deployment (web tier, app tier, DB tier). Today it's used interchangeably with "layered."
Why It's Everywhere

The Reasons It Stuck

Easy to Onboard

Show a new engineer the folder structure and they immediately know where things live. controllers/, services/, repositories/. Every framework's official tutorial uses some version of it.

Frameworks Cooperate

Spring, ASP.NET, Django, Rails, NestJS — every major framework has annotations, scaffolds, and conventions for layered code. @Controller, @Service, @Repository map directly.

Swap Below, Not Above

If presentation talks only to the service layer, you can add a new front end (REST, GraphQL, gRPC, CLI) without touching domain or data. If services talk only to repository interfaces, swapping the persistence backend is bounded.

Where It Hurts

The Classic Failure Modes

The Anemic Domain Model

Domain entities become bags of getters and setters; all the behavior leaks into "services" that read fields and decide. The "domain" layer ends up with no domain in it. Sound familiar? This is the layered pattern's most common failure.

Antidote: push behavior back onto entities. account.debit(amount) instead of service.debit(account, amount).

Layer Leakage

Controllers query the DB directly "just for this one endpoint." Repositories return ORM entities that the controller serializes straight to JSON, leaking schema to clients. Without enforcement, the lines blur and the architecture stops paying off.

Smearing Modules Across Layers

If you organize top-level by layer (controllers/, services/, repositories/), the billing module is smeared across all three folders. Reading "what does billing do?" requires opening three places. Modular monoliths flip this — top-level by feature, layers inside each module.

The Service Layer Becomes a God Object

"I need to add this logic somewhere — services, I guess?" Result: 5,000-line service classes orchestrating everything because nobody knew where else to put it. The pattern doesn't tell you when to split a service, so most teams don't.

Strict Layering Adds Boilerplate

Adding one field can mean updating: DTO → controller → service → mapper → entity → repository → DB schema. Six edits to add one column. Many teams quietly relax strict layering for this reason.

Variants

Three-Tier and Beyond

Classic Three-Tier (Physical)

Web servers, app servers, database — deployed on different machines. The browser hits a load balancer, which hits app servers, which hit the DB. Still the basis of most web architectures, even ones that call themselves something else.

MVC / MVVM / MVP

Presentation-layer patterns within layered architecture. MVC (Model-View-Controller) is the workhorse for server-rendered apps. MVVM binds views to view-models for data-binding-heavy clients (WPF, Vue). MVP adds a presenter that drives passive views.

Open vs Closed Layers

Closed: a layer can only call the one directly below. Strictest; most boilerplate.

Open: a layer can call any layer below it. Practical; risks turning into a free-for-all.

Decision

When to Pick Layered

Reasonable default for:

  • Small to medium business apps where the team values onboarding speed over domain purity.
  • CRUD-heavy systems where the domain is genuinely thin.
  • Anything where the framework's defaults are layered (Rails, Spring Boot, Django, ASP.NET MVC) — fighting the framework rarely pays off.

Reach instead for hexagonal / clean when the domain is rich, long-lived, and has to outlive multiple infrastructure choices. Reach for modular monolith organization when multiple bounded contexts coexist — slice top-level by feature, then keep layers inside each feature.

Continue

Other Architectural Styles