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.
It’s the Clean Architecture, Not a Clean Architecture
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.
So here’s what we’re going to do in today’s post:
- We’ll start today’s post not by explaining what clean architecture is right away.
- We’ll then dive into the problems that high coupling can cause in software projects.
- Next, 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.
Download the NDepend trial for free and see if your architecture is sustainable, let alone clean.
“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, following an architectural pattern. 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.
Want to learn more? Here is the next post in our clean architecture series.
At the risk of sounding nerdy, I’m excited about this. There might be a good “reference” example of Clean Architecture out there, but perhaps I didn’t Google hard enough to find it. So far I’ve just been able to try to apply some of the principles. I’m looking forward to seeing what this looks like built by someone who gets it.
Sounds nice, but is often totally impractical. Examples:
1. Suppose you only want certain options in the UI to appear depending on the setting of other elements in the UI which have a business purpose. This often requires coding business logic in the UI without implementing an inefficient process of communicating through layers, a process that usually affects the UI in a user-unfriendly way.
2. I work in a Microsoft shop, something I haven’t always been happy with, since I could have often done my work much more quickly with fewer bugs using open source technologies. I tried to make our code more database agnostic by using OLE DB adaptors, rather than SQL Server DB adaptors. The OLE DB adaptors, developed in an open environment of adhering to the least common denominator, dealt with stored procedure parameters numerically, ignoring the names. This contributed to hard to troubleshoot bugs until I discovered that the SQL Server adaptors actually took the parameter names into account and switched to them. The OLE DB was a waste of time and resources, especially since the use of open source databases was better implemented in non-Microsoft languages to begin with.
That’s appealing to the dreaming developers (read rookies) who think they can do anything without database knowledge.
Great article. Waiting for next part please.
Very nice article.
Some aspects i’m missing are:
– (G)UI is twofold: 1) Data to be handled by users (she/he needs to read it and take action on it and has to enter it from a sources he must have at hand) and needs syntactical and semantical checks, and 2) presentation and input of data to/from the user which depends e.g. on view ports, navigation necessities and alike to optimize the perception of users. These two aspects may not be defined in one layer.
– Interfaces to other systems are quite similar to UIs. They need their one layer.
All in all, I doubt that architectural layers can be “independent”. They must be commonly defined oriented on the class of tasks the apps produced with them.
You made me want to read the book actually ! I don’t really get the difference with DDD’s bounded contexts though, but I guess I’ll know more once I’ve read the book. That’s more or less the recipe I follow to make my code testable and avoid mocks. Aslak Hellesoy gave a talk describing similar ideas at Code Node : https://skillsmatter.com/skillscasts/9971-testable-software-architecture-with-aslak-hellesoy
Thanks
Thx for the nice article. Looking forward to read the follow ups. In fact I also just started a blog series to dive deeper into Uncle Bob’s clean architecture https://plainionist.github.io/Implementing-Clean-Architecture/
Clean Architecture appears to be a higher level Design Pattern, that uses SOLID programming principles to fill in where actual Design Patterns are not useful, or needed. Overall there is the use of the dependency inversion principle, and the architecture actually calls out for adapters. Model View Controller is a design pattern too. I have been interested in proper code architecture, and only recently come across Clean Architecture. It will be interesting to integrate Clean, DP, and SOLID in the future.
“It happened to me on my first job after college.”
I’ve heard this argument before in defence of attempts to make your DAL swappable. It totally misses the point.
The problem with trying to implement swappable DALs is not that it never happens, but that it incurs a substantial overhead for day to day development that your stakeholders may not be happy about having to pay for, while not being supported by a shred of real-world evidence that it actually delivers the benefits that it claims to deliver.
If your clients were actually asking you to design your codebase so that you could swap out your database for some unknown mystery alternative at a future date, and you were able to provide them with some actual evidence or case studies showing that Clean Architecture could actually deliver that objective in practice, and you told them — honestly — how much extra it would add in terms of development time as compared to other approaches (2 layer, 3 layer, CQRS, microservices etc) then it might be a different matter. Most articles about Clean Architecture that I’ve seen say nothing whatsoever about doing this.
Best practices such as separation of concerns are a means to an end, not an end in themselves. Any best practice or architectural choice needs to justify itself in terms of addressing requirements that your clients or stakeholders are actually willing to pay extra for. Any that make things harder or more cumbersome without doing so are stealing from the business.
In any case, swapping out your database is always going to be hard, involving months of planning and testing not only in your application code, but also in terms of infrastructure, operations, migrations, support, monitoring, downtime management, performance considerations and a whole lot more. There’s far more to it than just sliding out one IRepository and replacing it with another one. On top of that, different databases very often work in completely different ways, with different performance considerations, different ways of allocating primary keys, different ways of handling cross joins, different approaches to paging and sorting, you name it. A naive interface around Entity Framework may be difficult if not impossible to implement transparently against RavenDB, a web service, or a DNA sequencer.