|$ curl https://forge-ai.dev/api/markdown?path=docs/architecture/layered
$cat docs/layered-/-n-tier-architecture.md
updated Today·18-25 min read·published

Layered / N-Tier Architecture

ArchitectureLayeredN-TierBeginner🎯Free Tools
Introduction

Layered architecture organizes code into horizontal layers — commonly presentation, application/domain, and data access — with a dependency rule that higher layers call lower layers, not the reverse. N-tier often maps those layers to separately deployable tiers (browser, app servers, database).

It is widely taught and easy to explain. It helps when teams need clear technical separation and auditability. It hurts when business complexity is the main problem and layering becomes ceremony that scatters domain logic.

Common Layers

Presentation / UI / API

HTTP controllers, GraphQL resolvers, CLI. Translates external protocols to application calls. No business rules beyond input validation shaping.

Application / use case

Orchestrates domain operations, transactions, authorization checks at use-case level.

Domain

Business rules, entities, domain services. Should not depend on frameworks or SQL.

Persistence / infrastructure

ORM, file storage, external HTTP clients. Implements interfaces defined inward.

Classic strict layering sometimes collapses domain into "services" that are anemic — entities as data bags, logic in service classes. Prefer keeping invariants in the domain model when complexity warrants it.

Dependency Rules
  • Dependencies point inward/downward: UI → application → domain ← infrastructure adapters.
  • Lower layers must not import UI types.
  • Domain must not import ORM annotations if you want framework independence (hexagonal strengthens this).
  • Cross-cutting concerns (logging, auth) via explicit mechanisms — not random imports upward.
layer-rule.txt
TEXT
1Allowed: api → app → domain
2Allowed: infrastructure → domain (implements ports)
3Forbidden: domain → api
4Forbidden: domain → orm driver directly (prefer ports)
📝

note

Hexagonal / Clean Architecture is a stricter evolution of dependency inversion around the domain.
When Layering Helps
SituationWhy layers help
Large junior-heavy teamClear place for new code
Audit / compliance mappingControls mapped to layers
Stable CRUD systemsPredictable structure
Separating UI from data accessReplace presentation without rewriting SQL
When Layering Hurts
  • Domain is complex — horizontal slices scatter a single business change across many folders.
  • Every feature requires touching all layers mechanically (pass-through methods with no logic).
  • Layers become dumping grounds ("UtilService", "CommonManager").
  • Physical N-tier distribution without need adds latency and failure modes.

warning

If most application services are one-line delegates to repositories, you have ceremony without benefit — consider modular monolith by capability or hexagonal use cases.
How to Apply Well
  • Keep validation of business invariants in domain, not only controllers.
  • Do not let controllers access repositories directly if you need a use-case boundary.
  • Combine with vertical modules: layered inside each module, not one global layer cake.
  • Enforce with lint/arch tests.
  • Document the dependency rule in an ADR.
usecase.sketch.ts
TypeScript
1// Application layer orchestrates; domain enforces rules
2export async function placeOrder(cmd: PlaceOrder, deps: {{
3 orders: OrderRepository;
4 prices: PricingPort;
5 events: EventPort;
6}}) {{
7 const total = await deps.prices.quote(cmd.items);
8 const order = Order.create(cmd, total); // domain invariants
9 await deps.orders.save(order);
10 await deps.events.publish("OrderPlaced", order.id);
11 return order.id;
12}}
N-Tier Deployment

Logical layers need not map 1:1 to network tiers. Many systems keep presentation+application+domain in one process and treat DB as the second tier. Adding tiers requires requirements: separate scale, separate security zones, or separate teams managing each tier.

RequirementTier implication
Browser + API + DB onlyClassic 3-tier is enough
DMZ / internal segmentationExtra network tiers / gateways
Independent scale of renderingSeparate BFF or SSR tier
Patterns Inside Layers

Controllers should translate and authorize at the edge, then call application use cases. Repositories encapsulate queries. Domain objects enforce invariants. DTOs crossing layer boundaries prevent leaking persistence models to APIs.

LayerDoesDoes not
PresentationMap HTTP ↔ DTO; authn contextEncode pricing rules
ApplicationTransaction boundary; orchestrationRaw SQL everywhere
DomainInvariants; policiesKnow Express/Django
InfrastructureIO detailsDecide business outcomes
Evolving Layered Systems

Many codebases start layered and grow painful. Evolve by introducing vertical modules while keeping layers inside each module — a modular layered monolith. Alternatively, push toward hexagonal ports at the domain boundary when framework coupling hurts.

  • Identify the hottest change sets that touch every layer — candidates for module extraction.
  • Introduce interfaces at infrastructure boundaries incrementally.
  • Delete pass-through services that add no policy.
  • Add archunit/dependency rules before large refactors so regressions fail CI.
End-to-End Example Flow
layered-flow.txt
TEXT
1HTTP POST /orders
2 → OrdersController (presentation): validate shape, map DTO
3 → PlaceOrderHandler (application): begin tx, load entities, call domain
4 → Order.place() (domain): enforce min items, compute totals
5 → OrderRepository (infrastructure): INSERT rows
6 → OutboxPublisher (infrastructure): write event row
7 ← return order id DTO

The same flow in a poorly layered system often has SQL and pricing math inside the controller — fast to write, expensive to change safely.

Layered Architecture Checklist
layered-checklist.txt
TEXT
1[ ] Dependency rule documented and enforced
2[ ] Domain free of framework imports (or ADR justifying exceptions)
3[ ] Controllers do not access DB session directly
4[ ] Business invariants have domain tests
5[ ] Physical tiers justified by requirements
6[ ] Combined with vertical modules if product is large
Requirements Mapping
RequirementLayered implication
Swap UI toolkitPresentation isolation pays off
Complex domain rulesInvest in real domain layer or go hexagonal
Simple CRUD adminThin layers OK; avoid over-abstraction
Separate security zone for DBPhysical tiering may be justified

Write an ADR when you choose layered as the house style so newcomers know the dependency rule and where business logic must live.

Layered vs Modular by Capability

Pure layering organizes by technical role; modular monoliths organize by business capability (with optional layers inside). Large products usually need both: capabilities as the primary cut, layers as the secondary cut.

Team & SDLC Notes

Layered architecture pairs well with onboarding checklists and code review rubrics ("which layer does this belong in?"). In Agile teams, still deliver vertical slices — implement across layers per story rather than finishing the data layer for all features first (a Waterfall trap inside an Agile calendar).

Community

Get help on Slack, Discord or VIP

Stuck on a guide? Join the community and ask.