A few months ago I stumbled upon a blog post by Robert C. Martin entitled “The Clean Architecture”. The author demonstrates how classes can be divided into layers, depending on how central they are to the problem at hand, thus leading to better separation of concerns and modularity. I think that the ideas presented in the article are really valuable, and would therefore like to share my thoughts on how they might apply to the platform I deal with most of the time, which is iOS.
The concept of Clean Architecture
Clean Architecture can be concisely presented with the following picture:
As we can see, there are four layers objects in our system belong to. This is not a hard rule, that we always have to adhere to. Everything depends on the project we’re dealing with, so there might be situations where we have five layers or even more. Let’s take a quick tour to see what each layer is responsible for:
- Entities – these are the objects that encapsulate high level business rules that are least likely to change. The same entities may be used in many different systems.
- Use cases – objects that deal with business rules specific to the system we are working on. This layer will be affected by changes made to entities.
- Controllers, gateways, presenters – they are called “Interface Adapters” because they serve as the glue between the layer above and the layer below. They facilitate communication between use cases and external dependencies such as web services or a database.
- Devices, Web, UI, DB – this layer contains the tools and frameworks that are most likely to change. They are not central to the solution and often serve as a communication channel.
The arrows in the picture above represent an important rule affecting all the layers: source code dependency goes from the outer layers to the inner layers. This means that, for example, types belonging to “Use Cases” can have references to types enclosed in “Entities”. Communication from inner layers to outer layers must be done via interfaces/protocols. Such approach ensures, that classes that are less likely to change, are not tightly coupled to classes that are more likely to change.
Now let’s take a look at what Clean Architecture might look like in the context of an iOS application.
Put a stop to Massive View Controller
The first class that we are definitely going to need is a subclass of UIViewController. It belongs in the blue layer in the diagram above, as it deals with the UI of our app. It is important to remember that a view controller should be very simple. The only responsibilities it has, is managing a view hierarchy and informing other objects of events happening as a result of user input.
It’s a good start, but we’re not going to achieve much with just a view controller. Since it should not handle user input on its own, we need to create a class our view controller can communicate with. This class will be placed in the green layer in the architecture diagram and we will call it “Presenter”.
Now the view controller can inform the presenter of any events triggered by the user or the ones that are a part of a view controller’s life cycle, like viewDidLoad.
As we can see, the new diagram contains arrows representing references between objects. The connection going from the controller to the presenter is a strong reference and the controller knows the type of the presenter. The opposite connection is a weak reference (we don’t want any retain cycles) and the presenter only knows a protocol the controller implements.
What responsibilities does our presenter have? Its main role is to act as an adapter between the view controller and the next class we’re going to talk about, called “Interactor”. Presenter can, for example, transform domain models into structs that can be directly consumed by the controller and displayed on the screen. It would also be a good idea to give it the responsibility of holding state of the UI. This could be indexes of rows in a UITableView that have been selected by the user.
So who does the actual work?
The previous paragraph shows that Presenter works on tasks related to the UI of our system. The core logic of our application is something “Interactor” deals with. This class encapsulates the use cases that we identified in the initial stages of our project. It operates on “Entities”, which comprise the domain model and contain business rules pertaining to that model. Here is how Interactor fits in the object graph we have so far.
References between objects are set up in the same way as in the previous step. The presenter holds a strong reference to the interactor and knows its type. The interactor holds a weak reference to the presenter and does not know its type – it communicates via a protocol.
What about the data?
Our object graph is getting more and more complicated, but we’re still missing a vital part. It would be useful if our app was able to fetch data from different sources and save it locally. That is where a class called “Entity Gateway” enters the stage. It’s responsibility is to abstract away such concepts as a database or a web service. Interactor does not need to know where the data comes from, it just knows that it comes from an Entity Gateway. Apart from serving as a data source or data sink, this class is also responsible for translating objects used by databases or web services into objects in our domain model.
Interactor must not know the concrete type of Entity Gateway, but only a protocol this object implements. This is not a requirement on the reference pointing in the opposite direction.
The missing link
We are almost finished with adding new classes to our object graph, but there is still one missing. We need an object that will create all the others and make all the necessary connections between them. We are going to name it “Router”. We can think of Router as an object that creates other objects and decides what to present to the user when navigation elements are tapped on the screen. For example, when the user taps on a row in a table view, information about this fact travels from Controller, through Presenter and ends up in Router. Router decides what objects to instantiate to present more content to the user.
As far as the connections go, Presenter has a strong reference to Router and Router has a weak reference to Controller. This connection is necessary because Router is going to manipulate the view controller hierarchy in response to user input.
In this blog post we were looking at what Clean Architecture might look like, when implemented in an iOS application. We ended up with a graph consisting of five different types of objects. This may seem like a lot, considering the fact that those five objects correspond to one (usually) screen in an application. On the other hand, we gained clear structure and good separation of concerns, both of which make our code easier to understand, test and modify in the future. If you want to know how all this can be applied in practice, make sure to read my next article in which we will take a look at a concrete example in Swift.