Software Architecture

What Is Layered Architecture? Separating Software Concerns

What Is Layered Architecture? Separating Software Concerns

A layered application splits its code into horizontal slices. Each slice handles one responsibility and depends only on the slice below it.

Mark Richards’ four-layer version names presentation, business, persistence, and database.

You’ll mostly find the pattern in enterprise CRUD applications built on Spring MVC and ASP.NET Core. Architects usually weigh it against hexagonal, Clean Architecture, and microservices designs.

The layers are logical groups of code, not deployment units. Microsoft’s Azure Architecture Center defines tiers as the physical units that run them, and says several layers can share one tier (as of October 2026).

What Is Layered Architecture?

YouTube player

Code gets grouped by what it does technically, such as showing screens or storing data, and not by which business feature it belongs to.

In his O’Reilly report Software Architecture Patterns, Mark Richards puts it at the top of five foundational styles in software architecture. The other four are event-driven, microkernel, microservices, and space-based.

Microsoft’s Azure Architecture Center treats it as the traditional style for enterprise applications and files it under the name n-tier. The names overlap, but layers and tiers are not the same thing.

It isn’t a deployment model. Layers organize code, and where that code runs is a separate decision. It also doesn’t fix a layer count, and it isn’t tied to one language or framework, since it shows up in Java and .NET projects alike.

Which Layers Does a Layered Architecture Have?

YouTube player

Richards’ report names presentation, business, persistence, and database. Martin Fowler’s 2015 essay on presentation, domain, and data layering counts three (presentation, domain logic, and data access), and Microsoft’s .NET architecture guide lists the same three as typical (UI, business logic, and data access).

The disagreement is whether the database itself counts as a layer. Baeldung says the count depends on the architect, and Richards’ report states that the pattern sets neither the number nor the types of layers.

What drives the global software industry?

Uncover software development statistics: industry growth, methodology trends, developer demographics, and the data behind modern software creation.

Discover Software Insights →

Presentation holds the screens and API controllers, and it handles input. The business layer is where the rules, calculations, and workflow decisions live. Persistence takes care of queries and the mapping between objects and tables. The database is the physical store, usually relational.

Each layer answers to one concern, which is separation of concerns applied to the structure of the code.

Placing an order shows how it plays out. The request lands in the presentation layer. The business layer checks stock and applies promotions, persistence writes the order, and the database holds on to it.

In web frameworks, the presentation layer usually follows the MVC pattern, with controllers handling requests and views rendering responses.

Inside the persistence layer you’ll keep running into repositories, which expose stored data through a collection-like interface. Data access objects (DAOs) wrap raw database calls. Data transfer objects (DTOs) carry data between layers so database entities don’t leak out.

Some teams add layers when four don’t fit, and a shared services layer is the usual extra.

How Do Requests Move Between Layers?

YouTube player

Requests go down one layer at a time, and results come back up the same path. Closed layers have to be passed through, and open layers are the ones callers are allowed to skip.

Microsoft’s Azure Architecture Center puts the dependency rule in one line: a higher layer can use services in a lower layer, but a lower layer cannot use services in a higher layer.

Interfaces keep that direction intact in code. The upper layer depends on an abstraction (the dependency inversion principle), and a dependency injection container supplies the concrete class at runtime, so a service never builds its own repository.

Closed layers

Every request passes through a closed layer on its way down. Richards calls this layers of isolation, meaning a change inside one layer does not reach past its neighbor.

  • The controller receives POST /orders and hands the payload to the service
  • The service validates stock, applies the rules, and calls the repository
  • The repository writes the row to the database
  • The result returns up through the same layers

The cost shows up on simple requests. Azure’s n-tier documentation notes that a closed layer architecture can create unnecessary network traffic when a layer only passes requests along to the next one.

Open layers

Callers are allowed to bypass an open layer and reach the one below it. The legitimate case is a shared services layer that sits below the business layer and holds common components, logging and auditing classes for example, that only the business layer should reach. Mark it open and the business layer can skip it and call persistence directly. Richards’ report uses this example.

The unplanned case is a presentation component querying the data access layer directly. Microsoft’s patterns and practices guidance gives this as the common example of relaxed layering, and notes that data maintenance applications often work this way.

The first is a design decision and the second is drift.

Deciding which layers to open

Keep layers closed by default. Open one only when you can state why bypassing is safe.

A layer is a fair candidate for opening when it would only forward the call, or when it holds shared services that the layer above needs to skip past. Leave it closed if it holds business rules or security checks that every request has to pass.

Write the decision down. Richards warns that undocumented open and closed layers produce tightly coupled, brittle systems that are hard to test, maintain, and deploy.

Logical Layers Versus Physical Tiers

Layers are logical groups of code. A tier is a separately deployed unit, usually running on its own machine or subnet.

AttributeLayerTier
What it isCode grouped by responsibilityDeployment unit on its own infrastructure
Where it livesPackages, namespaces, foldersServers, VMs, subnets
Cost of separatingNone at runtimeNetwork latency, more infrastructure

The Azure Architecture Center states that several layers can share one tier and that a layer does not need a tier of its own (as of October 2026). The .NET architecture guide adds that an N-layer application deployed to a single tier is common.

A new ASP.NET Core project shows the single-tier case. It’s one project and one deployable unit, with layers kept apart by folders, per Microsoft’s guide to modern web applications.

A traditional three-tier split runs a presentation tier, then an optional middle tier, then a database tier.

Physically separating tiers improves scalability and resiliency, but adds latency from the extra network calls, according to Azure’s n-tier documentation.

Terminology is not settled. Microsoft’s patterns and practices Application Architecture Guide uses layer for a logical grouping of components and tier for physical distribution, while Core J2EE Patterns (2nd edition) defines a tier as a logical partition.

Check which meaning an author uses before comparing advice.

Because most layered applications ship as one unit, they behave like a monolithic architecture at runtime. NDepend’s 2018 write-up makes the same point: a layered application is deployed as a whole, even when its namespaces are cleanly separated.

Advantages and Disadvantages of Layered Architecture

The pattern buys simplicity and pays for it with flexibility. It’s easy to build and explain, and the pain starts when the application grows and needs to scale or change.

What works in its favor

Focused layers are the main draw. Separation of concerns keeps each layer to one job, and TechBeacon’s summary of Richards’ report lists maintainability and testability as the payoff.

Parts are replaceable too. An app that hides SQL Server persistence behind an interface can later move to cloud-based persistence without touching the layers above (Microsoft .NET architecture guide). Baeldung lists cost overhead as fairly low, and DevIQ adds that new developers recognize the structure quickly.

Testability is the advantage teams notice first. Each layer gets its own unit testing with the layer below replaced by a stub, so a business rule test never touches a database.

Where it hurts

The downsides are mostly about change. A feature that cuts across the application touches every layer, and the whole thing scales and deploys as one unit.

Extra layer hops add overhead, and Microsoft’s patterns and practices guidance warns that crossing too many component boundaries hurts performance. Since everything flows toward the database, the design ends up database-centric (NDepend, 2018).

Sources disagree on scalability. Azure’s n-tier documentation says physical separation of tiers improves it, while NDepend and Baeldung describe scaling as difficult because the whole application has to scale together.

Both are right about different things. Tiers scale independently when deployed separately, and a single-unit layered application does not.

The Architecture Sinkhole and Other Failure Modes

YouTube player

The architecture sinkhole is the signature failure of layered architecture. Requests pass through layer after layer without any layer adding logic.

Take GET /users/42/email. The controller forwards to the service, the service forwards to the repository, and the repository returns one column.

Two of those hops added a method call and a mapping step, nothing else.

Richards’ 80-20 rule works as the test. Roughly 20% pass-through requests is normal, and when that ratio reverses and 80% pass straight through, the layering is overhead, not structure.

To run the audit, list the endpoints the application serves, trace each one, and mark the layers that only forward the call. Then divide the pass-through endpoints by the total.

The fix is rarely another layer. Let simple reads skip the business layer through an open path and keep writes closed, because writes are where the rules live.

Upward dependencies erode many of the benefits of layering, per Microsoft’s patterns and practices guidance. Fat controllers are another common problem, where business rules end up in the presentation layer and the business layer holds nothing worth testing.

Drift is slower. Each shortcut looks harmless on its own, and DevIQ describes relaxed layering, where a layer skips intermediate ones for performance or convenience, as a variation of the pattern. Left long enough, the application grows into a large, tightly coupled monolith (DevIQ).

Each violation is small. Left alone, they pile up as technical debt that surfaces later as slow, risky changes.

Removing a pass-through layer is a plain code refactoring job. Delete the forwarding methods and point the caller at the next layer that does real work.

When Layered Architecture Fits and When It Does Not

YouTube player

It fits small to medium CRUD applications built under time or budget limits. It stops fitting once independent scaling or per-feature releases drive the design, or once the domain logic gets rich enough to outgrow one business layer.

Enterprise CRUD applications where most requests read or write records are the obvious fit, especially with teams organized by layer, from UI developers down to DBAs.

The Azure Architecture Center also points to applications whose architectural requirements are still evolving, and to on-premises applications moving to the cloud with minimal changes (as of October 2026).

Systems that must scale one part independently are a poor fit. So are products with frequent, independent releases per feature, and domains with dense business rules tend to outgrow a single business layer anyway.

The scaling levers for a single-unit layered application are horizontal and vertical scaling of the whole unit. Neither helps when one hot path in one layer is the bottleneck.

Run the sinkhole ratio on real endpoints, then ask whether one deployable unit can carry the expected load. If both answers come back yes, stay layered.

Martin Fowler’s 2015 Monolith First note reports that many of his colleagues advise against starting a new project with microservices, even when the application will grow large.

For a plain CRUD service that meets its load, a layered monolith is the sensible default, not a stopgap.

Layered Architecture Compared With Microservices, Hexagonal and Clean Architecture

YouTube player

Hexagonal, onion, and Clean Architecture invert the dependency direction so the domain sits at the center, while microservices split the system by business capability into separately deployed services.

PatternDependency directionBest fitMain weakness
LayeredDownward, layer to layerCRUD, small to medium appsSinkhole, drift
HexagonalInward, through portsApps that must run without UI or databaseMore interfaces and adapters
OnionInward, toward the domain modelLong-lived business appsHeavy for small sites
CleanInward, toward the application coreTestable core, swappable infrastructureMore projects to maintain
MicroservicesNetwork calls between servicesLarge systems, mature DevOpsCost and risk premium

Alistair Cockburn described hexagonal architecture in 2005. iSAQB’s SAG 2025 interview with him presents it as a way to separate core application logic from external dependencies, and Cockburn himself describes it as inside versus outside rather than a stack of layers.

Jeffrey Palermo’s 2008 Onion Architecture post adds internal layers around the domain model and sets one rule, that all coupling points toward the center. NDepend’s 2018 write-up says his goal was to fix problems the layered architecture faced.

Sources disagree on whether these are separate patterns. Microsoft’s .NET architecture guide treats hexagonal, ports-and-adapters, onion, and Clean Architecture as names for one approach, while Cockburn and Palermo each describe a distinct emphasis (ports for one, concentric domain layers for the other).

Microsoft’s eShopOnWeb reference application organizes its code into projects using Clean Architecture, with the application core depending on no other layer.

Eric Evans’ domain-driven design keeps layering but splits the business layer into application and domain layers, which is why it pairs with the inward-pointing patterns.

Microservices work differently. Lewis and Fowler’s 2014 definition of microservices architecture treats a component as a unit that is independently replaceable and upgradeable, which services deliver and in-process layers cannot.

Azure’s architecture styles page says microservices need mature development and DevOps practices (as of October 2026), and Fowler’s 2015 Microservice Premium note says the style adds cost and risk. A single microservice can still be layered inside, so the two patterns combine.

Roughly, it breaks down like this.

  • Go layered for plain CRUD with one team and one deployable unit.
  • A rich or long-lived domain with heavy testing needs points to hexagonal, onion, or Clean Architecture.
  • Choose microservices only when business capabilities need independent scaling and releases and you have the DevOps maturity to run them.

How to Implement and Enforce Layers in a Spring Boot or ASP.NET Core Project

YouTube player

Map each layer to a package or project and inject dependencies downward through interfaces. Then turn the dependency rules into a test that fails the build.

  1. Define the layers as packages (Spring Boot) or projects (ASP.NET Core)
  2. Inject downward only, so the controller takes a service and the service takes a repository
  3. Keep entities behind the persistence boundary and map to DTOs at the API edge
  4. Write the layer rules as an executable test with ArchUnit or NDepend
  5. Run that test on every build so a violation blocks the merge
  6. Test each layer on its own, then add a few end-to-end checks

Project structure

YouTube player

In Spring Boot the usual packages are controller, service, repository, entity, and dto, with @RestController, @Service, and @Repository marking each role. Spring Data repository interfaces are detected automatically, so @Repository is optional there, while DAO classes keep it for persistence exception translation per Spring Framework’s stereotype annotations.

ASP.NET Core usually gets Web, Business, and DataAccess projects, where Web references Business and Business references DataAccess. A lower project cannot reference an upper one without a circular reference, so the compiler rejects upward dependencies.

The mistakes that show up most are business logic written in controllers and repositories injected straight into controllers. Returning entities from endpoints is another, since it ties the API to the database schema.

Enforcing dependency rules

A diagram does not stop anyone from adding an import. A rule that runs in the build does.

ArchUnit analyzes Java bytecode, and its layeredArchitecture() API defines layers by package and states who may access whom, such as the persistence layer being reachable only from the service layer (ArchUnit user guide, version 1.5.1 as of October 2026).

NDepend ships a default CQLinq rule for .NET that flags any UI type using database framework types such as ADO.NET, Entity Framework, or NHibernate directly (NDepend documentation).

Both tools run in continuous integration, so a violation fails the build instead of waiting for a reviewer to notice.

Neither tool judges value. A service that only forwards calls still passes the rule, so the sinkhole ratio check stays a human job.

Testing each layer

  • Business layer tests are plain unit tests with no Spring context and the repository stubbed
  • The @WebMvcTest slice loads controllers only, with the service mocked
  • The @DataJpaTest slice loads repositories against an embedded database and rolls each test back
  • A handful of @SpringBootTest runs cover the full request path

Business rules are the cheapest to cover. Mocking the repository lets a test call the service directly, with no database and no web server involved.

Layered Architecture FAQ

How does the business layer differ from the domain layer?

In Richards’ four-layer model, the business layer holds every rule. Domain-driven design (Eric Evans) splits it. The domain layer keeps business rules and state, and a separate application layer coordinates use cases and transactions without owning rules.

Is layered architecture the same as MVC?

No, though sources overlap them. TechBeacon’s summary of Richards’ report calls MVC a layered architecture, but MVC divides user-interface responsibilities into model, view, and controller, while layering divides the entire application by responsibility.

Does the OSI model count as layered architecture?

Yes. The OSI model stacks protocol layers where each layer serves the one above and relies on the one below, and Baeldung lists it as a real-life application of the pattern.

TCP/IP follows the same idea.

Does layered architecture always mean a monolith?

No. A monolith describes how code is deployed, and layering describes how it is organized.

A modular monolith can use feature slices instead of layers, and layered code can run as separate tiers.

Is layered architecture outdated?

No. NDepend’s 2018 article calls it a still-solid approach, and the Azure Architecture Center still recommends the n-tier style for moving existing layered applications to the cloud with minimal changes (as of October 2026).

It loses where domain complexity or independent scaling dominates.

How do you move a layered monolith to microservices?

Split by business capability, not by layer. Lewis and Fowler describe microservices as organized around business capabilities, so extract one capability at a time with its slice of every layer.

Fowler’s 2015 Monolith First note advises keeping the monolith modular until splitting pays off.

What Keeping Layer Boundaries Intact Costs Over Time

Keeping layer boundaries intact in a layered architecture costs a standing check in the build and a periodic review of pass-through requests.

The order matters. Start with the audit of pass-through requests, then encode the layer rules as a failing build check, and open layers last, only where the audit found waste.

The audit goes first because a rule written before it locks wasteful layers into the build. Rules go before opening layers because a bypass without a check invites the next one.

The ongoing price is upkeep, since rules written against package names, as ArchUnit’s are, need an edit whenever a package is renamed or split.

Checked against the ArchUnit documentation (version 1.5.1) on October 30, 2026. A new layer API would change the second step.

When the audit keeps failing, the next option is ports and adapters, covered in this guide to hexagonal architecture.

Bogdan Sandu

Stay sharp. Ship better code.

Every week: one curated article, one tool worth knowing, one tip you can use tomorrow. No noise, no padding.