A constant and consistent approach to code writing is an important element of design implementation. It will have an impact on all the work as well as on the lead time. In today’s article, I will take a look at the Clean Architecture approach. What is the main idea behind the creation of a Clean Architecture-type solution, and in what situations will it work out? I will also try to discuss and present its main advantages.
Choice of architecture
When starting a new design, developers want to finish the first task very quickly, disregarding the importance of preparing the design. This is a mistake! After all, it is the initial stage of creating a new app that is the most important. It makes it possible to develop a consistent and constant approach to be adopted by other developers involved in the design.
Architecture versus technical debt
Another mistake made by developers is to bring about technical debt. It is true that we often create something quickly, and it would seem sensible… were it not for the fact that due to various factors we incur a growing technical debt. In design work, it is not worth cutting corners. Haste and negligence are the most common reasons for the accumulation of technical debt in a design.
It is not so bad if we incur a debt that we can “repay” later, i.e. make timely corrections. It is much worse, however, if we incur a debt that exceeds our skills or exceeds the time frame of the design.
To avoid this problem, it is worth using one of the numerous design architecture creation patterns available. Clean Architecture is my favorite, and also the approach I use most often.
Clean Architecture is an approach to app programming that involves separating the four main functional modules in a design. These modules are:
In the following part of the article, I will discuss each of them in detail. To begin with, I would like to say a few words about the main idea behind the Clean Architecture approach.
The idea behind Clean Architecture
In developing the Clean Architecture approach, its originator sought to create a design structure with a clear separation of responsibilities between individual layers. The assumption was that each of the layers should perform one task and be easy to isolate from the rest. Another assumption was to be able to share one code between multiple environments. This would be done by “pulling out” two main modules that are independent of the technology used to create the app, namely the Presentation and Domain modules, and then sharing them in other environments, while adding only the Application and Data layers. These latter layers are closely related to the environment in which we run the design. A good example of this is writing business logic just once and sharing it through mobile iOS and Android apps as well as through web-based and desktop apps.
Clean Architecture – design patterns
The presentation module can be implemented using many popular design patterns: Model View ViewModel (MVVM), Model View Presenter (MVP), Model View Intent (MVI), Model View Controller (MVC), and many more. Every programmer has their own preferences when it comes to choosing a pattern. Although every pattern has its own advantages and disadvantages, we will focus on the MVP approach to describe the architecture in question. This is the approach that I personally prefer.
The app layer is most strongly linked to the technology where we use the Clean Architecture approach. It provides the entire visual part of the design and responds to the tasks entrusted to it by the presentation layer. This layer determines how the data provided by the presentation part will be displayed. It contains the display logic and informs the presentation layer of any actions taken by the user. It also supports the mechanism of maintaining the Presenter’s status and prepares all the necessary dependencies for its proper operation.
The Presenter controls the entire app as well as the individual screens, and also reacts to individual actions taken by the user in the visual part. It is also there that the decision is made on how to handle a given task or what part of the app should be shown sequentially to the user. UseCases (described below) are the only way that the module can communicate with others. They are used, for instance, to execute business logic or to download specific data. In the MVP approach, every Presenter has an interface to communicate with the view, and this communication only makes it possible to delegate tasks to the view, but it does not enable requesting anything in return. Simply put, the Presenter executes functions on the view interface, but these functions cannot have the returned type.
The domain is a module that contains the entire business logic. It consists of many components:
The most important, externally visible component, which is used by the presentation layer. UseCases are the representation of real business requirements and provide the presentation logic with access to data operations (downloading, changing, and listening for changes). They also enable business logic execution – calculations or data manipulation. It is worth mentioning that they are the only possibility of communication between the presentation layer and the domain layers. Data are transferred by models stored in this layer as well. UseCases get access to data through DataSources – through the interface they provide. Another type of dependencies used by them are classes from the Logic layer (described below).
A set of logically collected data used by business logic. This model does not have to be identical to the model from the data layer. It can, for example, combine several data models, depending on the needs and application.
A place in the structure which is used for storing separate classes with advanced business logic. This makes it possible to avoid complex UseCases.
The most important task of the DataSource is to determine where the data will be retrieved from or where it will be saved. DataSources have access (via interfaces) to all data sources posted by the Data module. In addition to data, inside you will also find Mappers that make it possible to translate the data from the Data layer into business models used later on. DataSources are the only connection between the Data module and the rest of the business logic, and they post further interfaces providing access to adjusted data for UseCases.
Mappers in Clean Architecture ensure the integrity of data provided by the data layer. This is where verification takes place to ensure that all the necessary data has been delivered so that the business logic can fully operate. Mappers often combine several objects from the data layer, which originate from different sources, thus creating one object that contains all the data required by the business logic.
A module that provides access to all types of data sources and models that map this data. Access to the methods made available by this module is possible only by using the interfaces that this module provides. It is also the second module with the greatest dependency on the platform on which Clean Architecture is used.
Data Transfer Object
Data Transfer Object (DTO for short) is a model used for two-way data transfer. Such models are used only in the Data layer and, depending on whether we send or receive the data, they are translated into or from business models by means of the Mappers.
Data is a place where the data used in other parts of the app is collected and stored. Examples of data sources include: Bluetooth, the Internet, Shared Preferences, databases, files, GPS, camera, gyroscope, or the system. Access to such data is provided via an interface. This makes it easier to replace a given source. The replacement of such a source is done by specifying different objects implementing that interface. Replacement of data sources is most often used, for instance, to support different types of servers or to connect mock data.
Clean Architecture – disadvantages
- It is time-consuming. Implementing such complex architecture requires additional time. In my experience, writing code in accordance with the Clean Architecture approach requires about 10% more time than creating an app without any special architecture.
- It also requires knowledge. In this type of approach there is a difficult entry threshold for people who had no knowledge of the structure in question before.
- There is a large number of small classes and interfaces. Some people think of it as a disadvantage, while others think it is actually an advantage. It will be fair to say that there is a grain of truth in every opinion. My opinion is that this is undoubtedly an advantage – people who are of a different opinion believe that a large number of files is difficult to manage, and that the changes made in them are not clear enough.
- It is easier to maintain. Clean Architecture significantly simplifies design maintenance. Fixing bugs in this approach is very easy, and locating them requires less time.
- Better change management. This approach speeds up design changes. Thanks to small classes with separate responsibilities, it is very easy to modify the code to meet new requirements, or to add new functionalities without worrying about possible consequences in other parts of the design.
- A large number of small classes and interfaces. As I have mentioned before, opinions on that aspect vary. I think it is an advantage because – thanks to many small classes with separate functionalities – the code can be easily covered by tests.
In what types of design should Clean Architecture be used?
In my opinion, Clean Architecture is a perfect fit for medium and large designs. In the case of small designs, the complexity of the approach may result in an extra workload. Does this mean, however, that we should not use Clean Architecture in such cases? It is worth remembering that small designs develop rapidly in a natural way. The decision to apply the CA approach even to smaller designs is justifiable in my opinion. This makes it possible to avoid incurring the technical debt I have already mentioned, especially when the time overhead is small.
Referring to the concept of sharing two modules between many technologies, I think it is a great idea and it is getting closer and closer to implementation. So far, it has been difficult to achieve, but thanks to the popularization of the Kotlin programming language, we will soon have the first designs fully exploiting those assumptions. I hope I was able to outline the main ideas behind Clean Architecture and encourage you to apply it to your designs. A thoughtful choice of architecture will allow you to avoid technical debt, and this particular type of architecture – although it requires knowledge and more time for implementation – undoubtedly works well in the long run, in both large and small designs.