skip to content

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