Overview

4 Thinking in Layers: Separating the 'How' from the 'Why' at Scale

This chapter explains how layered architecture helps teams organize an application so that each part has a clear purpose. Building on earlier ideas like separation of concerns and SOLID, it shows how a system can be divided into logical layers that handle different kinds of work: presenting information, coordinating use cases, capturing core business rules, and dealing with external technical details such as databases or email. The central idea is to separate the “why” of the business from the “how” of the implementation, so the codebase stays understandable as it grows.

A major theme is the downward dependency rule: higher layers may depend on lower layers, but lower layers must never depend on higher ones. Data can move up and commands can move down, yet source-code dependencies should point only one way. This rule preserves loose coupling, improves testability, and makes it easier to replace the UI or infrastructure without rewriting core logic. The chapter also clarifies that layers are logical structures, while tiers are physical deployment choices, and it emphasizes that the architecture should be enforced through folder structure, separate projects, and automated fitness functions rather than left to developer discipline alone.

The chapter also addresses cross-cutting concerns such as logging, authentication, and caching, which cut across many layers and can easily create tangled code if handled carelessly. It recommends dependency inversion and, in some cases, aspect-oriented techniques to keep these concerns separate from business logic. Finally, it warns against common anti-patterns like fat controllers and anemic domain models, showing how they emerge when responsibilities are misplaced. The healthier alternative is a rich domain model with thin controllers and services, where business rules live in the domain and orchestration stays outside it, producing software that is more maintainable, testable, and resilient.

A visual look at Classic versus Modern Layered Architecture
Downward Dependency Visual showing dependencies only flowing in one direction.
The lower layers only accept tasks from the upper layers; they don’t assign them. Data can flow both ways, though.
In a clean architecture, dependencies must only flow downward to the adjacent layer. Breaking this rule by "skipping" layers or creating upward dependencies (e.g., the Infrastructure Layer talking directly to the UI) creates a tangled web that makes the system impossible to test or modify without breaking everything.
Cross-Cutting Concerns serve multiple layers in an application.
The Four-Layer Diagram Blueprint with Cross-Cutting Concerns Showing Command and Data Flows
Fat Controllers and Anemic Domain Models are two of the most common anti-patterns!
Grandma's Letter visual showing how each represents a layer.
An example of what this sketch might look like on your whiteboard or piece of paper

Recap

  • Layered Architecture is an architectural pattern that applies Separation of Concerns to an entire application by grouping code into horizontal layers based on responsibility.
  • Modern applications often use a four-layer model: Presentation (the UI), Application (orchestration), Domain (core business rules), and Infrastructure (database, APIs, etc.).
  • The non-negotiable "golden rule" is the Downward Dependency Rule, which states that layers must depend only on layers below them.
  • Layers are a logical concept for organizing code, while Tiers are a physical concept for deploying code on different machines.
  • Cross-Cutting Concerns (like logging or security) are handled by "cutting across" layers. We manage them cleanly by depending on abstractions (like ILogger) and using Dependency Inversion.
  • The most common layering anti-pattern is the "Fat Controller / Anemic Domain Model" combo, where all business logic is pulled out of the domain and into the presentation/application layer.
  • The solution is a "Rich Domain Model", where data and the logic that operates on it are encapsulated together, leading to a system that is far more testable and maintainable.
  • This concept is battle-tested and mirrors the 7-layer OSI model that has successfully run the internet for decades.

FAQ

What is layered architecture in a software system?

Layered architecture is a way of organizing an application into horizontal layers, where each layer has a clear responsibility, such as presentation, application, domain, or infrastructure. It helps apply separation of concerns at the whole-system level and makes the codebase easier to understand and maintain.

What are the common layers in a modern layered architecture?

The common modern layers are: Presentation Layer for UI and user input, Application Layer for orchestration and use cases, Domain Layer for core business rules and state, and Infrastructure Layer for database access, external services, email, logging, and other technical concerns.

What is the downward dependency rule?

The downward dependency rule says that dependencies may only point downward: a higher layer can depend on a lower layer, but a lower layer must never depend on a higher one. This prevents tangled code, supports separation of concerns, and keeps the architecture maintainable.

What is the difference between command flow and data flow?

Command flow goes downward, meaning upper layers tell lower layers what to do. Data flow can go both directions, meaning lower layers can return results back up. The key point is that data may travel upward, but source code dependencies must still point downward.

How do layers differ from tiers?

Layers are logical separations in code based on responsibility, while tiers are physical separations in deployment across machines or processes. You can have multiple layers on one tier, or distribute those same layers across several tiers.

Why should a layered architecture use separate projects or folders?

Using folders or separate projects makes the architecture visible and enforceable. In compiled languages, separate projects can physically prevent forbidden references. In folder-based systems, the structure still helps guide design, and automated checks can enforce the boundaries.

What is a cross-cutting concern?

A cross-cutting concern is a responsibility like logging, authentication, caching, or error handling that is needed by multiple layers. It cuts across the normal horizontal structure and should be handled carefully to avoid tangling business logic.

How can cross-cutting concerns be handled without breaking layering?

A common approach is to define an abstraction, such as a logger interface, and inject it into the layers that need it. This keeps the layers loosely coupled and avoids hard dependencies on concrete technical implementations.

What are the Fat Controller and Anemic Domain Model anti-patterns?

The Fat Controller happens when the controller or service layer takes on too many responsibilities, such as validation, business rules, persistence, and emailing. The Anemic Domain Model is the result, where the domain becomes a data bag with little or no behavior. Together, they create low cohesion, poor testability, and brittle code.

Why is a rich domain model better than an anemic one?

A rich domain model keeps business rules and behavior inside the domain objects where they belong. This improves encapsulation, testability, and maintainability, because the domain becomes the single place that owns the business logic instead of scattering it across controllers and services.

pro $24.99 per month

  • access to all Manning books, MEAPs, liveVideos, liveProjects, and audiobooks!
  • choose one free eBook per month to keep
  • exclusive 50% discount on all purchases
  • renews monthly, pause or cancel renewal anytime

lite $19.99 per month

  • access to all Manning books, including MEAPs!

team

5, 10 or 20 seats+ for your team - learn more


choose your plan

team

monthly
annual
$49.99
$499.99
only $41.67 per month
  • five seats for your team
  • access to all Manning books, MEAPs, liveVideos, liveProjects, and audiobooks!
  • choose another free product every time you renew
  • choose twelve free products per year
  • exclusive 50% discount on all purchases
  • renews monthly, pause or cancel renewal anytime
  • renews annually, pause or cancel renewal anytime
  • Grokking Software Architecture ebook for free
choose your plan

team

monthly
annual
$49.99
$499.99
only $41.67 per month
  • five seats for your team
  • access to all Manning books, MEAPs, liveVideos, liveProjects, and audiobooks!
  • choose another free product every time you renew
  • choose twelve free products per year
  • exclusive 50% discount on all purchases
  • renews monthly, pause or cancel renewal anytime
  • renews annually, pause or cancel renewal anytime
  • Grokking Software Architecture ebook for free