When to use the Clean Architecture?

Enthusiasm, doubt, opposition

There are few possible reactions after learning about the Clean Architecture or Hexagonal Architecture (AKA Ports & Adapters) or even merely innocent service layer in Django. Some developers are enthusiastic and try to apply these techniques immediately, some are hesitant, full of doubts. The rest is strongly opposing, declaring openly this is an abomination. Then they say we already have excellent tools, like Django. Then they argue others don't know about the advanced features of common tools. Then they call you Java developer in disguise.

As a speaker and an author of the book Implementing the Clean Architecture , I have faced all the reactions from this spectrum. What two extremes fail to do, is to ask the right question - WHEN? When the Clean Architecture should be used?

Frankly, the Clean Architecture (or other technique) is not what most overenthusiastic adopters think it is. On the other hand, headstrong opponents see this as a bluff and try to call it. There is no bluff. It is not meant to be a silver bullet.

By the way, if you are an evangelist of one of these techniques and do not mention they are not one-size-fits-all solutions, start immediately. Please.

No silver bullet

So the Clean Architecture is like a hammer - applicable for a specific set of problems. And no, it does not deprecate our current toolset, in particular Django.

Speaking of silver bullets... during my talks about the Clean Architecture, I quote No Silver Bullet - Essence and Accident in Software Engineering - a paper written by Frederick Brooks. You can easily find it online. The clue is that there is no magic trick, single technique or approach (title silver bullet) that will make the efficiency of software development to improve dramatically.

The other worthy takeaway from the paper is a division of complexity into two categories - accidental and essential. Accidental complexity is something we can eradicate with a finite effort, e.g. refactor code, use 3rd party library (so we don't have to implement the solution entirely on our own). So it can be dealt with. Essential complexity is a completely different creature. It can not be avoided UNLESS we negotiate a change in business requirements.

Essential complexity will catch you

In other words, if business requirements are complex, so will be the code - no matter how many programming aces one has up their sleeve. If a program has 50 features, it will be inherently complex. This gives us the first reason to interest in the Clean Architecture which is...

Managing essential complexity

Assuming one failed to reduce the scope of business requirements, essential complexity is inevitable. Since we cannot avoid it, we have to learn how to manage it. One is no longer able to model a solution with a database browser-like application.

On the other hand, if one does not have to manage essential complexity, the Clean Architecture is simply unnecessary. And it makes things worse because that's an extra burden. This is introducing accidental complexity.

How to tell if one is dealing with essential complexity? Talk with the right people, the ones who call the shots. The ones who drive business requirements. Remember, they learn along the way the same way you do.

What you are looking for is called Core Domain (Domain-Driven Design terminology). Core Domain is a set of business rules that are the main reason for the project to be built. Take cloud services that provide authorization & identity management, like Auth0. For our projects, login/logout is rarely something we want to sweat over. It is critical, but they are quality libraries for that and look, cloud services. What one project may treat as Generic Domain (DDD again), for others is a core of their business. Auth0 site even claims that "Identity is Complex". Well, for them it certainly is. And they make it easy for others.

Core Domain is often part of the project that stands for a competitive advantage. If it is going to outperform competitors, simple solutions may not suffice. So the only piece of advice I can give is to recognize different aspects or functionalities the project you work on has or is meant to have.

Where else to look for essential complexity?

Core Domain may not be the only place which deserves more sophisticated techniques. There is a relatively easy heuristic to tell:

  • if it's not just a proxy over 3rd party, tightly coupled to it
  • if it's more than a simple database browser and uses more business validation rules
  • if it uses 3rd party, but it can be potentially swapped (like Payment Gateway)
  • if it just has a lot of edge cases and business rules to enforce...

the chances are you have a perfect match to apply the Clean Architecture or other technique.

An example: our project had a feature of subscriptions. A member:

  • could subscribe to one of the available plans (9 in total, subject to change in the future),
  • could choose one of the billing periods (monthly, yearly),
  • was to be charged periodically based on billing period,
  • could upgrade and downgrade their plan at any time,
  • would receive various benefits, affecting other parts of the application, including Core Domain

Even though it was not the main feature, still quite complex and crucial - because it was making extra money.

But you won't catch many of such subtleties unless you listen and talk with stakeholders or their representative, e.g. product owner/project manager. Do it, your effectiveness and possibly a success of the project depends on it. And someone thought that being a programmer means less human interaction. Which smoothly brings us to the second reason for using the Clean Architecture...

Facilitate communication

For simple CRUD-flavoured applications, one can be comfortable with translating business requirements into database tables operations on the go. Once the project grows and more essential complexity appears, it is no longer that easy.

That's pure biology. Our brains are too small to comprehend too much information at once. Which makes it harder and harder to represent domain knowledge in the code if it gets scattered in all corners of the application. That's why we focus on business rules and provide special places to put the logic.

Communication is crucial for effective work, for example reducing the risk of misunderstanding or increasing chances for discovering edge cases. It will also help you find out if the problem is essentially complex (use the Clean Architcture) or not.

In the end, we talk and think in a more abstract way. We think and code Entities or Use Cases. Eventually, data has to be saved in the database and read from it, but it's not our main concern. We realise it's more like a side effect.

Software Development is a learning process, Working code is a side effect.

Alberto Brandolini

"But this is not Pythonic! You're imitating Java/C# etc in our pure language!"

(Yes, I actually heard this twice and a friend of mine at least once).

It is not a matter of programming language, it is ALWAYS a matter of what problem one tries to solve with it. Then, only after recognizing the problem, one uses appropriate techniques.

If there was some mastermind, responsible for dispatching projects to companies specialized in particular technologies, perhaps all we would be getting were projects where Django shines. But it doesn't work that way. And that's good, really good. You are challenged, so you have to learn new things and as a result, we all thrive.

Look at the history of node.js. A few years back no one would suppose that this funny and often astonishing (see WAT talk by Gary Bernhardt) browser language would be a considerable competence for Python on the backend. Yeah, the language lacked a proper ecosystem, it was dynamically and weakly typed. Look what's happening now.

Now, look at Python. It also advanced in the last decade. We have type annotations, mypy, dataclasses just to name a few. Finally, dependency management got some traction (pipenv, poetry, PSF financed pip development...). Languages progress is simply a response to the needs of the market. Or rather it is a future created by contributors who see a need for tools that would make their life easier while working on real-world projects. Python is no different than other languages in that matter.

All this is fine, but the majority of my project does not have that essential complexity

The truth is that the Clean Architecture is not applicable throughout the entire project. Luckily, we are not doomed. It does not mean we have to choose between overengineering in most places or underengineering in our Core Domain.

It is just an indication of another need. Need for modularization. We need a way to use different architectural styles throughout the project. It doesn't really matter if it will be a modular monolith or microservices. We just need a little flexibility to choose the most appropriate style for different parts of the project.

However, it is unlikely you will ever see a need for modularization (not to mention the Clean Architecture) if you switch companies or projects every few months (say, about 2-3 months, definitely less than 6). You will not have enough exposure to a single business domain to learn intricacies. You will simply have no occasion to learn. While software developers tend to think at some stages of their careers that clean code can save the world, the fact is our code is merely a derivative of communication with stakeholders. Programming skills are a cure only for accidental complexity.

Should I bother even if I don't need the Clean Architecture now?

Yes, you should. It will still advance you as a programmer. And if you are lucky, you will work on more challenging projects in the future without sweating too much.

Even if it's not helpful for you now and you decide you don't want to commit to it, why not just remember there is such a technique and dive into it when there is a need for that?

This post is licensed under CC BY 4.0 by the author.

Comments powered by Disqus.