So, I semi-recently got into using the Notion app for collecting notes, links and doing task management, and I’ve been compiling collections of notes and bookmarks from several areas in my life, including programming. Being a prolific programming blog-reader and video-watcher, I’ve taken a bunch of small notes and saved a ton of bookmarks scattered all over the place, largely depending on the source. Anyway, as I was compiling notes on Clean Architecture specifically, I noticed something that I hadn’t noticed before about it, and I personally don’t agree with the idea.
Clean Architecture
First off, a quick rundown of Clean Architecture for those who don’t know, starting with the quintessential diagram:
Looking at it, you can see why it’s sometimes also called the Onion Architecture. It’s also interrelated to the Ports-and-Adapters Architecture, if you’re familiar with that. We’re going to focus on the left part – the circle part – for now. The important parts of the diagram are the arrows pointing inward and the labels of the layers. These arrows indicate that the outer layers depend on inner layers, which seems weird in some cases, but let me explain.
For example, it seems odd that UI or Web would depend on the Controller rather than the other way around, right? But the thing is: the Controller wants to be testable, so it defines how it wants its data from the outside to look so that you can stub it. The dependency arrow here is the web gateway or what-have-you providing data in that form or conforming to the interface defined there. Now, for the most part, we don’t end up thinking about this layer-to-layer dependency, since it’s usually defined by the framework we choose to use, but if we wanted, we *could* define our own Controller layer and make adapters in multiple frameworks that can use that layer. Doing so would kind of pointless in Clean Architecture, though, since the Controller layer is expected to be fairly dumb and merely provides a way to translate data from a UI or the Web into a form the Use Case (or Service, for DDDers) can use.
No, the typical focus is on the arrow between the Controller/Presenter/Gateway layer and the Use Case layer. Inner layers need to be able to interact with the outer layers (typically), which means the Use Case layer depends on the outer layer. So how do we get that arrow to point back in? Again, we define interfaces and required input types at the Use Case layer, and that dependency arrow to the outside world ends up staying in the same layer (and allows for test doubles of those types to be used, increasing testability), and the inward dependency arrow is the inheritance arrow pointing inward for implementing those interfaces.
This all works fine for me, and I feel like I understand it completely. So now we move on to the little diagram in the bottom right corner of the picture, showing the flow of control and something like a class diagram. First off, it shows that I apparently don’t understand the Presenter pattern correctly from my very quick glances at it years ago, so I’ll need to look into that more. I’ll pretend that it says Repository instead of Presenter, which is still well within the architectural design, and it simply makes the flow diagram incomplete.
The Problem
But there’s still something that’s weird to me: the Use Case Interactor inherits from a Use Case Input Port. The first time I noticed this, I figured it was something someone had added to the diagram at a later point, but after looking, it’s the same as one that was in an article written by the creator of Clean Architecture.
Why does the Use Case Interactor need to inherit from anything? In and of itself, it shouldn’t have any direct slow communication with the outside world (it does that via objects it defines interfaces for, so a test version can be substituted in), so there’s no good reason that it should ever need to be swapped out for a test version. I still haven’t finished reading through the Clean Architecture book, and the article mentioned earlier has been taken down or moved, so I can’t read the explanation for it easily.
The point of this article is that, even though I haven’t had the chance to read the possible “pre-written rebuttal” to this advice yet, I’m going to suggest that you don’t bother to create an interface for your use case interactors to implement. It’s a waste. The Use Case and Entity layers are meant to be the Core of your application; they stay the same, no matter what kind of technology you use to have it interact with the outside world or users.
This diagram shows my mental model of Clean Architecture just a little better:
It treats Entities and Use Cases as the Core (though it fails to wrap Entities with the Use Cases), and it shows which uses of outer layers require interfaces defined in the Use Case layer and which don’t. It also show how there’s a Configuration “layer” that builds this all and controls which implementations are to be used.
That’s all for today. If you’ve got the rebuttal I’m looking for, I’d love to hear it in the comments.