It seems to me that the topic of software architecture has attracted a lot of interest in the last few years. And among many different flavors and styles of software architecture, there’s one that attracts even more interest than the others. I’m talking about the clean architecture, proposed and evangelized by Robert C. Martin, a.k.a. Uncle Bob. (And for the rest of this post, it’s simply referred to as “clean architecture.”)
By employing clean architecture, you can design applications with very low coupling and independent of technical implementation details, such as databases and frameworks. That way, the application becomes easy to maintain and flexible to change. It also becomes intrinsically testable.
We’ll start today’s post not by explaining what clean architecture is right away. We’ll first dive into the problems that high coupling can cause in software projects. Then we’ll proceed to see the more mainstream solution to these problems.
Finally, we’ll explain the flaws with this solution, showing why and how clean architecture can be the piece of the puzzle that was missing for your application to achieve its full potential.
“Desperation” of Concerns
If you have at least a couple years of software development experience under your belt, then I can almost guarantee you’ve heard the term “separation of concerns.” It’s a principle that shows up quite often when talking about software architecture.
One of its incarnations is in the form of advice: “Separate presentation from business logic.” It’s very sound advice and I agree with it wholeheartedly, but the problem is that it’s not so easy or clear how to put it into practice, especially for beginners.
In practice, what ends up happening is that logic business frequently leaks to the application’s UI. And the same can go the other way around: it’s not so rare to find business logic code concerning itself with UI concepts, such as colors, markup languages, font sizes, and what have you.
And things can get even worse when you add data access to the mix. How does one free oneself from this mess?
Enter the Layered Architecture
The solution people often found for the mixing of concerns is to break up the application into layers. There are generally three:
- One layer concerns itself with everything database related. A common name for this layer is “data access layer” (DAL) or something similar.
- The next layer is responsible for the business logic of the application. It’s here that your entities and your value objects would generally reside, for instance. This is usually called, not surprisingly, “business logic layer” (BLL). Names such as “domain layer” or “core” are also common.
- Finally, we have the UI or presentation layer, which, ideally, should contain all the code related to presentation concerns and no business logic code at all.
Nothing groundbreaking here, right? This stuff is pretty standard. You can bet there are no shortages of applications running around the world right now that follow an architecture like the one I’ve just described. Maybe the names aren’t exactly the same or there are more (or maybe just two) layers, but the general principles still apply.
The Problems With the…Not So Clean Architecture
In order to see clean architecture’s benefits, we really need to understand the flaws in the current alternative. And for that, we need to talk about dependencies.
Quick question about the architecture described in the previous section: which layer depends on which? Well, it’s trivial to answer this if we organize the layers by using a diagram:
We can see that the presentation layer depends on (or references) the business logic layer, which is entirely reasonable. We wouldn’t want aesthetic tweaks to cause changes to our underlying business logic, right?
On the other hand, it’s perfectly acceptable that changes to the business rules cause changes in the UI.
The Traditional Layered Architecture Is Very Database-Centric
By looking at the diagram, we see that the business logic layer references the data access layer. If you rearranged the layers as concentric circles—like the common depiction of clean architecture, as we’ll see soon—then the data access layer would be at the center.
At first sight, it doesn’t look like a problem. After all, you often have to persist changes in the database after some interaction with the user. It seems like the natural workflow:
- User performs some request through the UI.
- The request generates some kind of action for the business objects to perform.
- Finally, changes are persisted to the database as needed.
Well, of course that’s pretty much what happens at execution time. What doesn’t follow is that the dependencies should flow in the same direction at compile time also. They shouldn’t. This will harm you. Let’s understand why.
The first problem is that you couple yourself to a specific database vendor. I already hear you saying how rare it is for this to happen in practice. It happened to me on my first job after college. I had to adapt an application that up until then only ran on SQL Server and make it support Oracle Database also. ORMs were out of the question, for…reasons. It was a tough job, but it could’ve been easier if the application wasn’t so heavily coupled to SQL Server.
Dirty Architecture Hurts Testability
The next point is about testability. It’s not uncommon to have data access code mixed up with code that performs calculations or other tasks that don’t rely on the database at all. You really wish you could test the logic of the code. But that would mean hitting the database, which would make the test not only slow but also harder to set up and less predictable.
Coupling to the database does not only harm automated testing. Even manual exploratory testing can suffer. As put by Alistair Cockburn:
“When the database server goes down or undergoes significant rework or replacement, the programmers can’t work because their work is tied to the presence of the database. This causes delay costs and often bad feelings between the people.”
Here he says “programmers,” but replace that with “testers” or “QA engineers” and the effect is pretty much the same.
This issue also applies to the UI layer. Sometimes you really wish you could test user interface, just for the sake of testing the presentation logic itself (e.g., when user clicks on radioButton, then the field x should be enabled) but without actually exercising the application logic. This would allow, for instance, that two completely separate teams handle the UI and business logic development.
Clean Architecture to the Rescue
Now we know that the problem with the traditional approach, in short, is that it creates unnecessary coupling. It couples the UI to the other layers; worse, it couples the domain (or business logic) layer to implementation details such as storage, which can have dire consequences for the testability of the application.
So, what’s the solution? Well, I’ve almost spelled it out in the previous paragraph: restore the domain to its rightful location, at the center of the architectural diagram.
Clean Architecture: A Bit of History
To the best of my knowledge, the first mention of clean architecture is from a blog post by Robert C. Martin, published back in 2011. He then proceeded to write another, more famous post on the subject in the next year. In this newer post, Robert provides a more formal definition of clean architecture, complete with diagrams.
Right at the beginning, Robert explains that the architecture he’s proposing isn’t necessarily new. Actually, it’s an attempt to integrate and create a shared vocabulary about several architectural ideas that had appeared in the years prior, such as onion architecture and hexagonal architecture.
Since then, he’s written more posts and given talks on the subjects, and it started gaining momentum. Other developers were inspired enough to create their own sample projects and examples based on clean architecture.
Characteristics of Clean Architecture
So, a quick recap. Up until now we’ve seen
- the dangers of a highly-coupled codebase;
- the traditional layered architecture commonly used to try to prevent those dangers;
- why the aforementioned architecture isn’t always a good solution; and
- a bit of history and background on clean architecture.
Finally, now it’s time to actually see what this whole clean architecture thing is about.
Layers and Organization
The common depiction of clean architecture is a diagram consisting of concentric circular layers, very reminiscent of the onion architecture, which is not a surprise. The idea here is that the inner layers are high-level, abstract policies; the outer layers are technical implementation details.
The proposed layers are
- Entities. Here should live the business objects of your application, generally called “entities,” in DDD lingo.
- Use cases. In this layer reside the use cases; in short, we could say that these are objects that represent an action a user can perform in the application.
- Interface adapters. This layer contains code whose goal is basically to provide a bridge between the outside world and the immaculate world inhabited by the use cases and entities. Models, views, presenters, and repository implementations all should go here.
- Frameworks and drivers. Finally, we have the layer that basically represents external agents: the web, the database, etc.
The Dependency Rule
The aforementioned layer organization isn’t set in stone, though. There’s nothing really stopping you from using more than that if you need to. What you really have to remember is to follow the dependency rule: all dependencies should point inwards.
The entities layer shouldn’t be aware of any other layer in the application. There should be no change in the outer layers that causes a change to it. The opposite holds, though: a change in the entities layer can and probably will cause changes in next layers.
Your Journey to a Clean and Safer Architecture Is Just Beginning
Today we’ve just touched the tip of the iceberg in regards to clean architecture. There’s a lot more to know about it, of course. My intention with this post was to present what might be a novel idea, to excite your curiosity and leave you wanting more.
In the next posts, we’ll continue to explore the subject here on the NDepend blog. We’ll build a sample application in order to put these guidelines into practice. And we’ll test these ideas against the most realistic scenarios we can create in a blog post.
We’ll also show you how you can use NDepend to help you achieve clean architecture even faster. Stay tuned!