This page looks best with JavaScript enabled

Architecture - Clean

 ·   ·  ☕ 6 min read

So clean you could eat off of it!

This is no revolutionary idea per se, but rather Uncle Bob’s take on the similarities within existing architectural patterns.
He saw a common thread between Ports & Adapters, Onion, EBI, plus a couple others and thought he would write an article as ‘an attempt at integrating all these architectures into a single actionable idea’.

Be Warned

I’ll take for granted you are familiar with the previously mentioned architectures going forward. If that’s not the case you’ll probably have a bad time.

High level

The original article starts with this diagram.

bobs-original-diagram

It gives an abstract and high level view on the general idea of the architecture.

However, I find there is information we don’t really need yet and tweaking a thing or two will make it easier to understand.

my-diagram

We can already see a couple of key points:

  • Heavy reliance on Ports & Adapters and Onion Architecture (just switch ‘Use Cases’ with ‘Application Service’).
  • Dependency goes inwards.
  • Independence from tools and delivery mechanism.
  • No anemic entities. To have them as our core business logic mandates that they be more than just POJO.

The diagram could drive some to structure the project directories like so:

Entities
├── Tenant
├── Landlord
├── Rent
Repositories
├── TenantRepo
├── LandlordRepo
├── RentRepo
UseCases
├── PayRent
├── RequestRent
├── RequestRepair
├── CalculateRent
...

However, as pointed out in this post, there are two glaring issues with this approach:

  • Low cohesion: Modify one Use Case, and you’ll have to change code in three different packages.
  • No clear purpose: A new developer to the project would have to dig through the directory structure to know what the application does.

The following approach should solve these issues and is actually more in line with Uncle Bob’s Clean Architecture as a whole (you’ll see why in a bit):

Tenant
├── Tenant
├── TenantRepo
├── PayRent
├── RequestRepair
Landlord
├── Landlord
├── LandlordRepo
├── RequestRent
Rent
├── Rent
├── RentRepo
├── CalculateRent
...

Cool, let’s get to the actionable part of the idea.

Low level

To fully understand that’s going on, we’ll need more than the diagram presented in the original blog post.
There’s another really useful one you can see in the slides Bob uses in his conferences.

bobs-flow

Again, really cool but a bit overwhelming. Let’s tone it down and build it back up bit by bit.

We’ll start from our entities and an Interactor. You should be familiar with this concept from what Bob calls ‘ebi architecture’.

base-diagram

In this context you can think of the Interactor as the core of a Use Case.
So if a Use Case is an abstract concept of ‘what needs to be done’, the Interactor is the concrete implementation of ‘how exactly it should be done’.

Once our Interactor is done managing its use case, we’ll probably want to persist our entities.

add-persistence-port

I’m taking for granted you know what a Port is, why it implies an Adapter and the difference Driving and Driven ports (if not, click here).
Since this one is driven, it’ll have blue borders.

Our Interactor will need some signal to tell it to do it’s thing.

add-input-port

This signal will come through some kind if input port (green borders mean it’s a driving Port).
I don’t care much what that input is: the web, a cli or another piece of software.

Now we probably want to establish some sort of output as well.
After all, whoever (or whatever) is using our system probably wants some feedback.

add-output-port

Lovely!
So far we’ve got the basic I/O for our system.
This includes what in the original diagram is described as the business logic and the application logic, so the two inner circles are taken care of.

Let’s make explicit what we already know.

arrows

We are slowly (but surely) completing the image!

Orange lines indicate inheritance while black ones indicate basic interaction.

Speaking of which, whatever input we receive through the Adapter will wind up interacting with our domain.
This requires some kind of agreement between Interactor and Port regarding what sort of model (as in data structure, DTO, POJOs, etc) they’ll be passing around.

Same goes for the outbound side.

models

Let’s make the names more descriptive and clearly mark the edge of our domain.

final-diagram

I’m leaving the upper right part open on purpose, since for the same entities there might be any given number of Interactors (one per Use Case, sometimes more, sometimes less).

Let’s review how the flow of execution would go. Say we have an incoming HTTP request:

  1. The Request reaches the Controller.
  2. The Controller:
    1. Dismantles the Request and creates a Request Model with the relevant data.
    2. Through the Boundary, it executes a method in the Interactor, passing it the Request Model.
  3. The Interactor:
    1. Finds the relevant Entities through the Entity Gateway.
    2. Orchestrates interactions between entities.
    3. Creates a Response Model with the relevant data and sends it to the Presenter through the Boundary.

The diagram at the lower right corner of the first diagram from the original post might help you visualize whats going on.
Just swap ‘Use Case Output/Input Port’ for ‘Boundary’:

flow

You can get the feel now that, as far as Uncle Bob is concerned, a Use Case is made up of an Interactor and its Boundaries.

Speaking of Uncle Bob, hopefully you should now be able to better grasp the diagram we started from.

bobs-flow

Only thing we missed are the View and View Model.

The View Model is a part of whatever front-end/framework we are working with that is only in charge of basic logic regarding how to display the data it receives from the presenter.

The View here is nothing more than the raw data visualization. Html and css in your typical web facing API.

Now we can see the remaining key points not explicitly clear in the first diagram:

  • Heavy reliance on EBI concepts: Even the names reference that architectural design.
  • Testability in isolation: You’ve probably noticed that you can mock every part of the system and that you don’t need any external tooling to run your test.

Conclusion

The original intent for this architecture was, as stated in Uncle Bob’s post, to create a system:

  • Independent of Frameworks.
  • Testable.
  • Independent of UI.
  • Independent of Database.
  • Independent of any external agency.

This is (hopefully) achieved by ‘standing on the shoulders of giants’, adding, clarifying and improving on some of the most important concepts in software architecture.

Support the author with