Software Dev Notes

Home › Software Architecture

How Layered Architecture Works: Layers, Boundaries and Dependency Direction

Learn how layered architecture splits an application into presentation, business and data layers, and why dependency direction decides maintainability.

2026-10-19 · 5 min read

Layered architecture is one of the oldest and most widely used ways to organize an application. The idea is simple: split the code into horizontal groups, called layers, where each layer has one job and talks only to the layers it needs. Most web applications, desktop tools and business systems use some form of it, often without naming it. This post explains what the layers are, how the boundaries between them work, and why the direction of dependencies matters more than the number of layers.

What a layer is

A layer is a group of code that solves one class of problem. The most common split has three parts. The presentation layer handles input and output, such as HTTP handlers, screen controllers or command-line parsers. The business layer, sometimes called the domain or service layer, holds the rules of the application, such as how an order total is calculated or when an account is locked. The data layer, also called the persistence or infrastructure layer, reads and writes storage such as databases, files or remote APIs.

The point of separating these concerns is not tidiness for its own sake. When the rules of the business change, you want to edit one place. When the database changes, you want the rules to stay untouched. Layers give those edits a home.

Boundaries are the real work

A layer is only useful if its boundary is respected. A boundary is the set of functions, interfaces or types that one layer exposes to another. If the presentation layer calls a business function that takes plain values and returns plain results, the boundary is clear. If the presentation layer builds raw SQL strings, the boundary has collapsed, and the business rules are now scattered across the UI code.

Good boundaries share a few traits. They expose intent rather than mechanics, so a method named placeOrder is better than one named insertRowAndUpdateCounter. They pass simple data across the line rather than live objects tied to a database session. They also keep error handling consistent, so the caller knows what kind of failure it may receive.

The dependency rule

The most important rule in layered design is about direction. Dependencies should point in one direction, usually from outer layers toward inner ones. The presentation layer may depend on the business layer. The business layer should not depend on the presentation layer. This is sometimes called the dependency rule, and it was popularized by Robert C. Martin under the name Clean Architecture, although the basic idea is much older.

Why does direction matter? Imagine the business layer imports a class from the web framework. Now the business rules cannot be tested without the web framework, cannot be reused in a command-line tool, and cannot survive a framework upgrade untouched. A single arrow pointing the wrong way creates a cycle of knowledge that spreads through the whole codebase.

To keep the business layer independent of storage, many teams define an interface in the business layer and implement it in the data layer. The business code asks for an OrderRepository with a method like save. The concrete implementation that talks to PostgreSQL or to a file lives outside, and is passed in at startup. This pattern is called dependency inversion, and it lets the arrow point inward even though the runtime call flows outward.

A worked example

Consider an online shop with three layers. The presentation layer exposes an endpoint that accepts a JSON body with a product identifier and a quantity. It validates that the fields exist, converts them to simple types, and calls the business layer. It then turns the result into an HTTP response with the right status code.

The business layer receives the product identifier and quantity. It checks stock, applies a discount rule, and decides whether the order is valid. It asks the repository for the stock level without knowing whether the number comes from a database or a cache. It returns either an order summary or a domain error such as "out of stock."

The data layer implements the repository. It runs the query, maps rows to simple records, and returns them. If the team later moves stock tracking to a warehouse service, only this layer changes. The discount rule and the HTTP response do not need to be touched.

Where layered design goes wrong

Layers can be overdone. Adding a layer for every small step creates pass-through code that does nothing except forward a call. This is sometimes called the lasagna architecture problem, where a simple feature requires edits in six files. A useful test is whether a layer adds a rule, a translation or an isolation that another layer would otherwise need. If it adds none of these, it may be noise.

The opposite mistake is the big ball of mud, where layers exist on paper but every file imports every other file. The fix is usually not a rewrite. It is to write down the allowed dependencies, check them with a tool, and fail the build when a forbidden import appears. Tools for this exist in most ecosystems, from ArchUnit in Java to import-linter in Python.

Choosing how many layers to use

There is no fixed number of layers that is right for every project. A small script may need none. A medium web service often works well with three. A large system may split the business layer into several modules, each with its own internal layers. The question to ask is always the same: what will change independently, and where should that change be contained?

Layer Main job Typically depends on
Presentation Parse input, format output Business layer
Business Enforce rules, coordinate work Interfaces it defines
Data Store and fetch records Business interfaces (implements them)

Note that the business layer depends on nothing outside itself. That is the design goal. Everything else is arranged around it.

Testing benefits

Layers make testing cheaper. Business rules can be tested with plain unit tests and in-memory fake repositories, which run in milliseconds and need no database server. The presentation layer can be tested with a stubbed business layer, so tests check only translation and status codes. Integration tests then cover the seams, where the real database and the real HTTP stack meet. This split keeps most of the test suite fast and focused.

Key takeaways

Layered architecture divides code by responsibility: input and output, business rules, and storage. The value lies in the boundaries between layers, which should expose intent and carry simple data. Dependencies should point inward toward the business rules, and interfaces defined by the inner layer let the outer layer plug in. Add a layer only when it earns its place with a rule, a translation or an isolation. Enforce the dependency direction with tooling, because a single wrong import can quietly undo years of careful structure.

More in Software Architecture

Architecture

Coupling and Cohesion: The Two Measures That Shape Maintainable Modules

Understand coupling and cohesion, the two classic measures of module design, with examples, a cohesion scale and practical steps to improve both.

6 min read