NDepend Blog

Improve your .NET code quality with NDepend

layered_architecture_solid_aproach_ndepend

Layered Architecture: Still a Solid Approach

February 7, 2026 9 minutes read

Layered architecture gets a lot of flack.

Even though it’s still the most prevalent architecture, we view it as an anti-pattern. It’s old, not scaleable, and anti-SOLID. It encourages (shudder) monoliths!

Yes, I know. Hexagonal architecture is the way to go. Or maybe I’m feeling a taste for onions. But only if it’s clean!

The point is that even though it may not be an object-oriented nirvana, layered architecture is still a useful pattern. And if done right, it paves the way towards more advanced designs and architecture.

So let’s talk about layers.

Layered architecture — also called n-tier architecture — is the most common software architecture pattern in enterprise development. It organizes an application into horizontal layers, where each layer has a single responsibility and depends only on the layer directly beneath it. The classic version has four layers: presentation, business, persistence, and database. That simplicity is exactly why it has outlived every trend that promised to bury it.

What Are Layers?

This is the easy part. Layers are logical separations in your code. Components are grouped in horizontal layers. Each layer has a specific function.

They are not tiers. And they do not require that all layers are physically separated. Additionally, layers don’t concern themselves with how your files are organized. (Though you probably should.)

Layers separate different responsibilities of your application. The presentation layer doesn’t know how data is stored, and the database layer doesn’t know anything about your UI. Each one minds its own business, and the layer above only talks to the layer directly below it.

That single rule — depend downward, never upward — is what makes the whole pattern work.

The Four Layers of a Layered Architecture

Most layered applications settle on the same four standard layers. You won’t always need all four, and you can certainly add more, but this is the template nearly everyone starts from.

  • Presentation layer. This is what the user touches: web pages, API controllers, view models, the desktop UI. Its only job is to take input and display output. No business rules live here.
  • Business layer. Also called the application or domain layer. This is where the rules live — validating an order, calculating shipping charges, deciding whether a customer qualifies for a discount. This layer is the reason your software exists.
  • Persistence layer. Sometimes called the data access layer (DAL). It knows how to read and write records: repositories, data mappers, ORM calls. It translates between your domain objects and whatever the database expects.
  • Database layer. The actual store — a relational database, a document store, or even a set of files. It holds the data and nothing else.

In smaller applications, the business and persistence layers often collapse into one. In larger ones, you might split the business layer into services and a domain layer. The number is up to you, which brings us to a question people get wrong constantly.

Layers vs. Tiers: What’s the Difference?

People use “layer” and “tier” as if they mean the same thing. They don’t, and the difference matters.

A layer is a logical separation of your code — presentation, business, persistence. It’s about how you organize responsibilities.

A tier is a physical separation — a process, a machine, a deployment boundary. It’s about where your code actually runs.

You can have four layers running in a single process on one machine. That’s a one-tier application with four layers. Split the database onto its own server and the UI onto a web server, and now you have a three-tier deployment of those same layers. Layers are about design; tiers are about infrastructure. Mixing the two up leads to a lot of confused architecture discussions.

How Does Layered Architecture Help Me?

There are some non-trivial benefits to organizing your logical structure into layers.

  • Your code will be easier to understand. Since this is one of the most widely used architectures, everyone has run into it and can follow it. The language used for most layers can be understood without much thought or explanation.
  • The pattern is easy to follow when writing new features. There isn’t a lot of mental gymnastics you have to do in order to understand how everything is tied together.
  • Testing is easier, as each layer is encapsulated and modular. Any calls outside the current layer should be using interfaces, which makes mocking in tests simple as well.
  • Your application is easy to extend. Whether it’s adding additional components or modifying existing ones, there’s an easy pattern to follow.
  • Onboarding is faster. A new developer who has seen layered architecture once — and almost all of them have — can find their way around your codebase on day one.

See below a picture of the NDepend Dependency Graph used to visualize its own layered code.

The NDepend Dependency Graph used to visualize its own Layered Code

Open Layers, Closed Layers, and the Sinkhole Trap

Here’s a detail most introductions skip, and it explains a lot of the criticism the pattern gets.

A closed layer can only be called by the layer directly above it, and it can only call the layer directly below. Requests move top to bottom, one step at a time. This is the default, and it’s what gives you “layers of isolation” — the idea that a change inside one layer doesn’t ripple into the others, because nobody is allowed to reach past their neighbor.

An open layer can be skipped. If you add a shared services layer that the business layer is allowed to bypass on its way to persistence, you’ve opened it. Open layers reduce pointless pass-through code, but they also weaken isolation — now more layers know about each other.

When you close layers without thinking, you can fall into the architecture sinkhole anti-pattern: a request enters at the top and falls straight through every layer, with each one doing nothing but handing it down untouched. If most of your requests are simple pass-throughs, you’re paying the full cost of the architecture and getting none of the benefit. The fix is the 80-20 rule — if roughly 80% of your requests do real work in each layer, you’re fine. If most are sinkholes, open a layer or rethink the design.

So What’s With All the Hate?

There are disadvantages to the layered architecture approach. And some aspects of it have been deemed not architecturally pure for domain-driven design.

However, there are disadvantages to most approaches. The key is to understand both the advantages and disadvantages. Pick what architectural patterns work best for your particular problem.

Here are some factors you want to keep in mind.

  1. The layered architecture is very database-centric. As mentioned before, since everything flows down, it’s often the database that’s the last layer. Critics of this architecture point out that your application isn’t about storing data; it’s about solving a business problem. However, when so many applications are simple CRUD apps, maybe the database is more than just a secondary player.
  2. Scaleability can be difficult with a layered architecture. This is tied to the fact that many layered applications tend to take on monolithic properties. If you need to scale your app, you have to scale the whole app! However, that doesn’t mean your layered application has to be a monolith. Once it becomes large enough, it’s time to split it out — just like you would with any other architecture.
  3. A layered application is harder to evolve, as changes in requirements will often touch all layers. Add one field to a form and you may end up editing the presentation, business, persistence, and database layers to carry it through.
  4. A layered architecture is deployed as a whole. That’s even if it’s modular and separated into good components and namespaces. But that might not be a bad thing. Unless you have separate teams working on different parts of the application, deploying all at once isn’t the worst thing you can do.

None of these are dealbreakers on their own. They’re trade-offs, and you weigh them against how much simplicity buys you on your specific project.

How Do I Keep Them SOLID?

So you’ve decided to use a layered architecture. What are some things you can do to keep your designs in line with SOLID principles?

Make Sure to Adhere to the Single Responsibility Principle

With the layered architecture, each layer has a specific responsibility. Now, what this doesn’t mean is that you have one service that takes care of all your business responsibilities for every function ever. As you may have noticed, each layer is also divided into components. These components also adhere to the single responsibility principle.

Components not only pertain to one feature’s responsibility; they also only have to deal with one facet of that responsibility. They deal with the presentation of your shipping charges or the business logic of calculating your shipping charges. Not both.

And components are made up of either subcomponents or classes. So what do these also need to adhere to? Single responsibilities!

Check Your Use of the Open-Closed Principle

The open-closed principle states that our objects should be open for extension but closed for modification. There isn’t anything inherent about layered architecture that would violate the open-closed principle. In fact, defining the components within layers can provide clear boundaries of where you can extend functionality.

Verify Your Hierarchy Follows the Liskov Substitution Principle

With Liskov’s substitution principle, classes should be replaceable with instances of their subtypes without altering the functionality. Now, as others here have pointed out, this one is broken quite often!

Fortunately, again, this is not one that’s too affected by layered architecture. You can break this principle just as easily with other architectural approaches.

Consider Your Clients by Using the Interface Segregation Principle

So what’s next on our SOLID path? The interface segregation principle states that

Clients should not be forced to depend upon interfaces that they do not use.

To ensure you’re not breaking this principle, keep your components small and interfaces to those components focused on their responsibility. Each layer in your architecture should be loosely coupled with the layers underneath.

When done right, only the adjacent layer will need to change when an interface contract changes.

And Step Lightly Around the Dependency Inversion Principle

A. Add interfaces between layers

Here things get a little tricky. First, what does the dependency inversion principle (DIP) require?

A. High level modules should not depend upon low level modules. Both should depend upon abstractions.
B. Abstractions should not depend upon details. Details should depend upon abstractions.

When looking at requirement A, it’s pretty simple. In its base form, dependency inversion is about relying on abstractions and not concrete implementations. That sounds easy enough — just use interfaces!

So what’s the catch?

Let’s look further. DIP also has a friend named “inversion of control” (IOC).

IOC occurs when the runtime and compile time of dependencies are opposite each other. Additionally, that point of inversion is a logical boundary between two different layers.

This is where we run into problems with the layered architecture. Since the data access layer is at the base of everything, it implies that it should own the data access interface. However, since the data access layer holds the details of data access, it shouldn’t actually own the interface. And if we’re following true DIP and IOC, we would end up with more of an onion or hexagonal architecture.

What Else Can I Do?

At this point, you might be asking if there’s anything that will make it easier to work with layers. Let’s discuss some options.

Choose the Right Number of Layers

The layered architecture does not predefine the layers you need. As a developer or architect, you have to decide what’s best for your application.

However, typically the answer is not “All the layers!” It’s three to six layers.

And remember that every additional layer you add makes the code more complicated and difficult to maintain.

Create the Proper Components

We can’t really get DIP from a layered architecture. So what can we do? We can make sure our components are properly broken down. We do that using namespaces.

Remember two important points.

First, don’t create mutual dependencies between layers and namespaces. No lower level namespaces should reference higher level namespaces. This leads to spaghetti code and tight coupling.

Second, avoid dependency cycles. You won’t be able to find these as easily. Dependency cycles occur when you have namespace A depend on namespace B. B then relies on namespace C. And then, oh, by the way, C relies on A. Using tools like NDepend will help find those cyclic dependencies and flush them out.

Layered Architecture vs. Hexagonal, Onion, and Clean

Layered architecture isn’t the only game in town, and the patterns it’s usually compared to all grew out of the same complaint: in a strict layered design, your business logic ends up depending on your database. Here’s how the main options stack up.

Pattern Dependency direction Best for Main trade-off
Layered (n-tier) Top down, toward the database CRUD apps, smaller teams, fast onboarding Business logic leans on data access
Hexagonal (ports and adapters) Inward, toward the domain Apps with many external integrations More moving parts up front
Onion Inward, toward the domain core Rich domain models, DDD Steeper learning curve
Clean Inward, dependencies point at use cases Long-lived, framework-independent systems More boilerplate and indirection

Notice that hexagonal, onion, and clean architecture are basically the same idea wearing different clothes: invert the dependencies so the domain sits at the center and depends on nothing. Layered architecture is the honest starting point. Get your layers and namespaces clean, and the jump to one of these is a refactor, not a rewrite.

When Should You Use Layered Architecture?

Reach for layered architecture when:

  • You’re building a straightforward CRUD or line-of-business application.
  • Your team is small, or new developers need to get productive quickly.
  • You want a low-risk starting point that can evolve into a stricter design later.
  • Time to market matters more than architectural purity.

Think twice when:

  • You need to scale individual parts of the system independently.
  • You have a rich, complex domain that deserves to sit at the center of the design.
  • Multiple teams need to deploy their pieces on their own schedules.

In those cases, you’re probably looking at microservices, or one of the dependency-inverted patterns above.

Layered Architecture FAQ

What is layered architecture?

Layered architecture is a software design pattern that organizes an application into horizontal layers, each with a single responsibility. A layer depends only on the layer directly below it. The classic layers are presentation, business, persistence, and database.

What are the four layers of a layered architecture?

The four standard layers are the presentation layer (the user interface), the business layer (the rules and logic), the persistence layer (data access), and the database layer (where data is stored). Smaller apps often merge business and persistence; larger ones may add more.

Is layered architecture the same as n-tier architecture?

The terms are used interchangeably, but strictly speaking a layer is a logical separation of code while a tier is a physical separation across processes or machines. You can run many layers on a single tier.

Is layered architecture still relevant?

Yes. It remains the most widely used pattern in enterprise software because it’s easy to understand, easy to test, and easy to hire for. It’s a sound default that can evolve into hexagonal, onion, or clean architecture as needs grow.

What are the main disadvantages of layered architecture?

It’s database-centric, it can be hard to scale because it tends toward a monolith, changes often touch every layer, and it’s prone to the architecture sinkhole anti-pattern when requests pass straight through layers without doing work.

Is layered architecture a monolith?

Not necessarily. Layered applications often grow into monoliths, but a well-factored layered design with clean namespaces and no dependency cycles can be split into separate deployables when it gets large enough.

Is That All?

Yes, that’s it. Just remember that your architecture works well in some situations. It also doesn’t work well in others. And the simplest architecture may not win any awards for purest style. But it can be easy to maintain and enhance. You just have to understand the basics.

Write clean code. Check your dependencies. And use tools to find the trouble spots.

This article is brought to you by the team behind NDepend — a proven .NET static analysis tool for improving code maintainability, security, and overall quality. Whether you’re modernizing a legacy .NET application or starting fresh in C#, get started with your free full-featured trial today!