Layered Architecture
Learning Objectives
Structure an application into presentation, application, domain and infrastructure layers.
Keep dependencies pointing inward/downward correctly; map DTOs; place validation.
Prerequisites: Modules 18, 21, 22.
Intuition and Definition
UI, HTTP controllers, CLI; input/output formats Application (service)
Use cases: orchestrate domain objects, transactions, security
Business rules, entities, value objects, domain services nothing (ideally)
Databases, messaging, file systems, external APIs Domain/application *interfaces*
In the classic version the domain depends on infrastructure (top-down); the improved version inverts that dependency — infrastructure implements interfaces declared by the domain/application — which leads directly to hexagonal and clean architecture.
Why It Matters
Layers separate concerns that change for different reasons: UI frameworks, business rules and databases evolve independently.
Syntax and Language Notes
Enforce layers with project/module boundaries (separate assemblies, Java modules, C++ libraries) and architecture tests (ArchUnit, NetArchTest).
Complete Example
#include <iostream>
#include <map>
#include <optional>
#include <stdexcept>
#include <string>
// ===== domain =====
class Reservation { // entity with rules
std::string id_; int seats_;
public:
Reservation(std::string id, int seats) : id_(std::move(id)), seats_(seats) {
if (seats < 1 || seats > 8) throw std::invalid_argument("1..8 seats per reservation");
}
const std::string& id() const { return id_; }
int seats() const { return seats_; }
};
struct ReservationRepository { // port declared BY the domain
virtual ~ReservationRepository() = default;
virtual void save(const Reservation&) = 0;
virtual int seatsTaken() const = 0;
};
// ===== application =====
struct BookRequest { std::string customer; int seats; }; // DTO in
struct BookResult { bool ok; std::string message; }; // DTO out
class BookingService { // use case
ReservationRepository& repo_;
int capacity_;
int next_ = 1;
public:
BookingService(ReservationRepository& r, int cap) : repo_(r), capacity_(cap) {}
BookResult book(const BookRequest& req) {
…201 Created: reserved R1 for Ana
409 Conflict: 1..8 seats per reservation
201 Created: reserved R2 for Lee
409 Conflict: fully bookedStep-by-Step Walkthrough
The domain Reservation enforces "1..8 seats"; it knows nothing about HTTP or storage.
The application BookingService orchestrates: create the entity, check capacity, save, build a result DTO.
The infrastructure implements the repository interface declared by the domain (dependency inversion).
The presentation layer maps results to HTTP status codes.
Validation lives in two places on purpose: format/required checks at the edge, business invariants in the domain.
flowchart TD
P[Presentation: handleHttp] --> A[Application: BookingService]
A --> D[Domain: Reservation, ReservationRepository]
I[Infrastructure: InMemoryReservations] -. implements .-> DCommon Mistakes
Business rules in controllers or in SQL stored procedures scattered across layers.
Domain depending on infrastructure (entities with ORM/HTTP annotations everywhere, new SqlConnection in entities).
Layer skipping (presentation calling the database directly).
Pass-through layers that add nothing (each layer just forwards calls) — merge them.
Trade-offs and Alternatives
Layers add mapping code (DTO ↔ entity) and indirection; for simple CRUD apps a thin "transaction script" design may be enough. Vertical slices (feature folders) organise by feature instead of by layer and are often combined with layering inside each slice.
Real-World Use
Most enterprise web applications (ASP.NET, Spring, Django), desktop apps (MVVM is a presentation-layer pattern), and embedded stacks (application / service / HAL / drivers — AUTOSAR's layered architecture).
Practice
Q1. (Easy) Which layer contains business rules?
Answer: The domain layer.
Q2. (Medium) Why does the repository interface live in the domain?
Answer: So the domain depends only on its own abstraction; infrastructure implements it (dependency inversion), keeping the domain independent of persistence technology.
Q3. (Advanced) Where should "email must be unique" be enforced?
Answer: It's a business rule spanning many entities: check it in the application service using a repository query (for good error messages) *and* enforce it with a unique constraint in the database (for correctness under concurrency).
Interview Question
Q1. "What goes in the application layer vs the domain layer?"
Answer: Domain: business concepts and rules that would exist even without software — entities, value objects, invariants, domain services, domain events. Application: use-case orchestration — loading aggregates from repositories, calling domain methods, transactions, authorization, sending notifications, mapping DTOs. A test: if it mentions HTTP, transactions or email, it's not domain; if it's a business rule a domain expert would recognise, it shouldn't be only in the application layer.
Quick Quiz
Q1. Name the four layers.
Answer: Presentation, application, domain, infrastructure.
Q2. What do DTOs do between layers?
Answer: Carry data across boundaries without exposing domain objects.
Q3. What is layer skipping?
Answer: A layer bypassing the next one (e.g. UI calling the database directly).
Summary
Separate presentation, application, domain and infrastructure; point dependencies at the domain.
Map DTOs at boundaries; keep business rules in the domain.
Related: Modules 21, 72, 73.