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 a diagram that can be simplified into this:
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:
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):
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.
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’.
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.
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.
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.
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.
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.
Let’s make the names more descriptive and clearly mark the edge of our domain.
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:
- The Request reaches the Controller.
- The Controller:
- Dismantles the Request and creates a Request Model with the relevant data.
- Through the Boundary, it executes a method in the Interactor, passing it the Request Model.
- The Interactor:
- Finds the relevant Entities through the Entity Gateway.
- Orchestrates interactions between entities.
- Creates a Response Model with the relevant data and sends it to the Presenter through the Boundary.
The diagram in the lower right corner of the diagram from the original post might help you visualize what’s going on.
Just swap ‘Use Case Output/Input Port’ for ‘Boundary’:
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.
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.