skip to content

Layering

Strict versus relaxed layering, the presentation/domain/infrastructure split, and the rules that keep a layer from reaching past its neighbour. The failure mode to recognise is the layer bridge that quietly turns a layered system into a ball of mud.

part ofSoftware design & architectureoverview, primer and where to startread it →
on this pageshow

questions

5

In a layered software architecture with presentation, domain (business logic), and infrastructure/persistence layers, what is each layer responsible for and in which direction may dependencies point?

level: juniorimportance: must knowfreq 85%

answer

  1. presentation → domain → infrastructure
  2. one-way references, no upward imports
  3. upward info via return values / events
  4. layers ≠ tiers (code vs. deployment)
  5. inverted form: domain at the center

basics

~20 s

Presentation handles input and output (UI, HTTP endpoints). Domain holds business rules and decisions. Infrastructure talks to databases, files, and other systems. Dependencies point one way — upper layers know about lower ones, never the reverse.

solid answer

~50 s

Layering groups code by technical responsibility. Presentation adapts the outside world to the application: rendering UI, parsing requests, mapping DTOs, formatting errors. The domain layer holds the rules — entities, invariants, use-case orchestration, transaction boundaries — and ideally contains no I/O or framework detail. Infrastructure implements the mechanics: SQL and ORM mappings, message brokers, HTTP clients, caches, file systems. The defining rule is dependency direction: compile-time references and calls flow one way, presentation → domain → infrastructure in the classic form. No lower layer may reference a higher one; the domain must not import a controller, and a repository implementation must not call business logic directly. Information flows back upward only through return values, domain events, or callbacks whose contracts are owned by the lower layer. That one-way rule is what makes the domain testable and lets you swap UI or storage independently. Without an enforced direction you have folders, not layers.

go deeper

for a junior

Name the three layers, say what belongs in each, and state that dependencies point one way with no upward imports.

for a middle

Add transaction and mapping responsibilities, explain how results flow back upward without upward dependencies, and distinguish logical layers from physical tiers.

for a senior

Contrast classic top-down layering with the dependency-inverted (hexagonal/clean) form, show where repository interfaces live, and discuss enforcement plus the indirection cost.

for a principal

Frame layering as one partitioning criterion among several, discuss when technical layering should yield to feature/module partitioning, and describe how the rule is enforced organizationally and in the build across many teams.

## What layering is **Layering** is a *structural style*: you partition a system into ordered groups of components ("layers") and impose a rule about which group may depend on which. In classic layering the partitioning criterion is *technical role* — what kind of work the code does — rather than *feature* (checkout, billing) which is the criterion used by vertical-slice or modular decompositions. A **dependency** here means any compile-time or link-time knowledge: an import, a type reference, a direct call, an inherited base class. "A depends on B" means A cannot compile or run without B being present. ## The canonical three (or four) layers 1. **Presentation / interface layer** — everything that adapts the outside world to the application. Web controllers, GraphQL resolvers, CLI commands, message-queue listeners, HTML templates, mobile views. Responsibilities: parse and validate the *shape* of input, authenticate/authorize at the edge, map request payloads (**DTOs** — Data Transfer Objects, dumb structures whose only job is to carry data across a boundary) into domain-meaningful arguments, and translate results and errors back into a protocol (HTTP status, JSON, screen state). 2. **Application layer** (often merged into the domain layer, sometimes separate) — orchestrates use cases: "place order" = load customer, check credit, create order, publish event, commit transaction. It owns transaction boundaries and sequencing, not business rules themselves. 3. **Domain / business layer** — the rules and the vocabulary of the problem. Entities, value objects, invariants ("an order total can never be negative"), policies, calculations, state machines. This is the layer whose value survives a rewrite of the UI or a database migration. 4. **Infrastructure / persistence layer** — technical mechanisms: SQL, ORM mappings, file and blob storage, HTTP clients to third parties, message brokers, email/SMS gateways, caches, clocks, random-number sources. Some teams also name a **data-access layer** separately from a broader infrastructure layer; the count matters far less than the ordering and the rule. ## The dependency-direction rule The rule is: **references flow in exactly one direction, downward through the ordering, and never back up.** Presentation may know about domain types; domain must not know that an HTTP controller exists. Infrastructure may know domain types (to persist them); it must not invoke business decisions. Why it matters: - **Testability.** A domain with no I/O dependency can be unit-tested in memory, with no database, network, or container. - **Replaceability.** You can add a CLI beside the web UI, or move from one database to another, touching only the layer that changed. - **Comprehensibility.** Cycles between layers make a system impossible to reason about or build incrementally; a one-way graph gives you a build order and a reading order. - **Change isolation.** Volatile things (frameworks, protocols, vendors) live at the edges; stable things (business rules) live in the middle. ## How information gets back upward Beginners often object: "but the repository has to tell the service something." Upward *information* flow is fine; upward *dependency* is not. Legal mechanisms: - **Return values and exceptions** — the caller pulled, so no upward reference exists. - **Callbacks / higher-order functions** the lower layer accepts as parameters. - **Observer or event notification** where the *event type* and the *listener interface* are owned by the lower layer, and the upper layer registers itself. Classic layering literature calls this the standard technique for layer-upward communication. - **Dependency inversion**: the *upper* layer declares an interface it needs, and the lower layer implements it. This flips the compile-time arrow while keeping runtime call direction. It is the basis of hexagonal/ports-and-adapters and "clean" architecture, where infrastructure depends on the domain rather than the reverse. ## Two orderings you will see - **Classic layering:** presentation → application → domain → infrastructure. Domain depends on the persistence abstraction. - **Dependency-inverted (onion/clean/hexagonal) layering:** domain sits at the center and depends on nothing; both presentation and infrastructure depend inward on the domain. The repository *interface* lives in the domain; the JDBC/ORM implementation lives in infrastructure. Both are "layered"; the second is strictly better for protecting the domain and is what most modern answers should describe, while acknowledging the classic form. ## Logical layers vs. physical tiers **Layers** are a code/compile-time concept. **Tiers** are deployment units (browser, app server, database server). A three-layer application very often runs in one process — layering does not imply network hops, and confusing the two leads people to think layering is inherently slow. ## Common failure modes - Domain classes annotated with ORM/serialization/framework annotations, so the "pure" layer silently depends on infrastructure. - Controllers containing business rules, leaving an anemic domain layer that is just data holders. - A repository interface whose method names leak SQL or paging details (`findByRawQuery`), so callers become coupled to storage. - Layers enforced only in documentation, with nothing in the build stopping a violation. ## Trade-offs Layering is cheap to teach, gives a stable place for everything, and matches most frameworks' defaults. It costs indirection: a trivial field addition can require edits in DTO, mapper, domain object, repository, and schema. It also groups code by *what it is* rather than by *what it is for*, so a single feature is smeared across the codebase — which is exactly the complaint that motivates vertical-slice and modular alternatives.

  • If the domain layer must publish an audit record to a message broker, how does it do that without depending on the broker library?
    It declares an interface it owns (e.g. `AuditPublisher`) expressed in domain terms; an infrastructure adapter implements it with the broker SDK and is wired in at composition time. That is dependency inversion: runtime calls still go downward, but the compile-time reference points inward to the domain.
  • Does layering require separate deployment tiers or separate services?
    No. Layers are a compile-time partition of code; tiers are deployment units. A single-process monolith can be strictly layered, and a distributed system can be a layering mess. Splitting layers across network boundaries adds latency and failure modes without improving the dependency graph.

Like a postal system: the counter clerk (presentation) knows the sorting rules (domain), and sorting knows the trucks and routes (infrastructure). A truck driver never decides postage rates; if a delivery fails, the truck files a report the sorting office already defined — it does not phone the counter clerk directly.

saying these in an interview costs you the question

  • Saying the database is the "bottom layer" that everything else is designed around, so schema shape dictates domain design.
  • Claiming layering means three physical tiers or three deployed services.
  • Believing a lower layer may call an upper layer directly "as long as it's just a notification" — the notification contract must be owned by the lower layer or inverted.
  • Treating packages named ui/service/dao as layering when nothing prevents a DAO from importing a controller.
  • Putting validation, authorization, and business rules in controllers and calling the resulting data-holder classes a "domain layer".

context

open as a page

What is the difference between strict (closed) layering and relaxed (open) layering, and what are the trade-offs of each?

level: middleimportance: must knowfreq 62%

basics

~20 s

Strict layering lets a layer call only the layer immediately below it. Relaxed layering lets it call any lower layer, skipping levels. Strict gives better isolation and swappability; relaxed avoids pointless pass-through code but couples more layers together.

open as a page

How can a domain (business) layer trigger database writes, emails, or messages without having any compile-time dependency on the database, mail, or broker technology?

level: seniorimportance: must knowfreq 58%

basics

~20 s

The domain declares interfaces describing what it needs ("save this order", "send this notification") in its own vocabulary. Infrastructure classes implement those interfaces, and something at startup wires the implementations in. Calls still go outward; the code dependency points inward.

open as a page

Name the common ways teams break a layered architecture in practice (layer-bridging and leakage anti-patterns) and explain why each is harmful.

level: seniorimportance: should knowfreq 48%

basics

~20 s

Typical breakages: controllers containing business rules, database entities used directly as API responses, lower layers calling upward, skipping the domain to hit the database, and pass-through layers that add nothing. Each one couples layers that should change independently.

open as a page

When does horizontal technical layering (presentation/domain/infrastructure) become a liability, and what structural alternatives — such as vertical feature slices or modular decomposition — would you choose instead?

level: principalimportance: should knowfreq 38%

basics

~20 s

Layering groups code by technical role, so one feature is spread across many folders and most changes touch every layer. When features change independently and teams own features, grouping by feature (vertical slices or modules) — with layering applied inside each slice — usually beats a single global layer stack.

open as a page