Clean Architecture

Chris FouchéChris Fouché
4 min read

Architecture is the art of drawing lines. A cheeky definition. I think of architecture as how concerns are separated and the rules that govern them. The lines we draw tend to depict this perspective. Clean Architecture is my go-to for source code. Here, I explain the gist of it and how I've made it my own.

School of Thought

There may be many clean architectures (in lowercase) out there. The one I'm referring to is Uncle Bob's Clean Architecture. Hence the capitals.

Ivar Jacobsen's ideas (Object-Oriented Software Engineering: A Use Case Driven Approach) influenced it. And it resembles Alistair Cockburn's Ports & Adapters (Hexagonal) Architecture.

Fun fact: Uncle Bob and Alistair Cockburn are two of the Agile Manifesto's seventeen authors. Legends. Let's explore their architectures.

The gist of it

Clean Architecture is about the separation of concerns. In particular, the separation of business rules from I/O concerns.

Business rules may not know about (depend on) I/O concerns. I/O concerns may know about the business rules.

I/O concerns

Input/Output (I/O) concerns are how your application gets and outputs data. It's the delivery mechanism of the data.

API endpoints, database integrations, integration to downstream systems or services (APIs), and user interfaces are all I/O concerns. Ways data flows into and out of your application.

Business Rules

Think of business rules as what the system must do, independent of I/O (data delivery) details.

Business rules include the code for use cases.

For example, consider a use case that says: 'A user must enter their email and password to register'. Notice it doesn't include I/O details — like capturing it in a React frontend, sending it to a .NET API, or saving it in a SQL database.

Very demure.

Whiteboard

Let's look at some lines.

Clean Architecture!

A prominent architectural boundary separates the business rules and I/O concerns. Notice how the source-code dependencies only cross the boundary in one direction: from the I/O side to the business rules.

How can a business-rule service call a class in an I/O layer if it's not allowed to depend on it? By inverting source code dependencies such that it points against the flow of control.

Instead of making a business rule service call an I/O class, we make it call a business rule interface. An I/O class implements that interface. Notice how the 'DbService' class implements the 'Repository' interface.

At runtime, an Inversion of Control (IoC) framework will 'plug' an instance of the I/O class into the interface, which acts as a port.

Ports & Adapters

Ports & Adapters (Hexagonal) Architecture works the same. It also has an interface at the point where an I/O concern calls a business-rule service.

Ports & Adapters!

Ports

There are two types of ports:

  • Primary: the interfaces that are called from the I/O side (and implemented within the business rules).
  • Secondary: the interfaces that are called within the business rules (and implemented in the I/O side).

Adapters

Adapters are the classes in the I/O layers that implement the ports.

Independence

Suppose the database needs to be replaced by a downstream API. In this architecture, we let a class in the integration (to downstream APIs) layer implement the secondary port instead. No other code changes needed!

Ports & Adapters!

Rules first

The business rules are the central organising principle. It drives the design.

Suppose you add a feature that'll include multiple layers. I recommend you start at the business rules layer. Develop and unit test it completely, first. Thereafter, add the other layers.

Try not to start at the I/O layers. Especially the database one! Its internal structure (such as a SQL database's relational model) may 'peer pressure' the business rules into looking and acting like it.

The Why

Clean Architecture reduces the long-term cost of software by making code easier to understand and change.

Here's the logic:

  • The easier it is—for us and our AI tools—to understand code, the faster we can change and test it, with precision.
  • If we can change one concern without affecting another, it's even faster.
  • The faster and safer the changes, the cheaper the software.

Clean Architecture does this with how it separates concerns.

The next generation

My Millennial and Gen Z peers and I built MonoLISA on the principles of Clean Code and Clean Architecture. MonoLISA guides how we do Clean Architecture in our backend monorepos.

Not only did we solve our problems. But we contributed to this school of thought, too. We understood the assignment!

Conclusion

Clean Architecture. Ports & Adapters. MonoLISA. It's how we enable ourselves and our AI tooling to quickly understand and change code, with precision.

3
Subscribe to my newsletter

Read articles from Chris Fouché directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Chris Fouché
Chris Fouché

Software Engineer at heart. Tech Lead by craft. With 16+ years in software engineering, I solve complex problems and enable the delivery of a continuous and efficient flow of value.