Book cover

Web Version | Dark Mode | Cite

Software Engineering: A Modern Approach

Marco Tulio Valente

1 Building Software with a Clean Architecture

1.1 Introduction

Clean Architecture is an architectural pattern proposed by Robert Martin—also known as Uncle Bob—that aims to achieve reusability, cohesion, technology independence, and testability. According to the classification used in Chapter 7, Clean Architecture can be considered a layered architecture.

Clean Architecture is typically illustrated using the following figure, which is based on a similar figure from a post on The Clean Code Blog.

Clean Architecture

In the following sections, we will examine each layer of the architecture.

1.2 Entities and Use Cases

At the center of the architecture, we have the classes responsible for business concerns, which can be of two types: Entities and Use Cases.

Entities are classes shared by many systems in an organization. For instance, consider a university with academic, financial, outreach, and other systems. All these systems have classes such as Student, Instructor, Course, Department, etc. These are called Entities. Besides data, entities can implement generic business rules. For example, a rule in our university might specify that every Instructor must belong to exactly one Department.

Use Cases are classes that implement operations relevant to the system domain. In the context of our university system, we would have use cases for creating a new class for a certain course, enrolling a student in a class, canceling a student’s enrollment, and recording the subjects covered in a class, among others.

1.3 Adapters

In the third layer, from inside to outside, we have classes and interfaces called Adapters. They mediate the interaction between the external layer of the architecture (External Frameworks) and the central layers (Use Cases and Entities).

Consider, for example, a system that uses a REST API for communication with the frontend components. In this system, the adapters implement the API’s endpoints. Specifically, they receive the requests and forward them to the corresponding use cases. Additionally, they are responsible for the reverse path: they receive the results returned by the use cases and convert them into JSON documents for transmission to the frontend.

1.4 External Frameworks

In the outermost layer, we have classes from libraries, frameworks, and external systems. For example, this layer includes databases, as well as libraries for sending emails, accessing payment providers, communicating with particular hardware, and other external services.

To illustrate, a university may have a system for managing extension courses that accepts payment via credit cards. This system uses a third-party provider, which offers classes for payment processing. These classes are therefore located in the outermost layer of a Clean Architecture.

In the Clean Architecture book (Chapter 22), this layer is described as follows:

The frameworks and drivers layer is where all the details go. The web is a detail. The database is a detail. We keep these things on the outside where they can do little harm.

1.5 Dependency Rule

In a Clean Architecture, the classes in a given layer should not reference any class implemented in an outer layer. Also in Chapter 22 of his book, Uncle Bob emphatically states that:

Nothing in an inner circle can know anything at all about something in an outer circle. In particular, the name of something declared in an outer circle must not be mentioned by the code in an inner circle. That includes functions, classes, variables, or any other named software entity.

Thus, in a Clean Architecture, the central layers are more stable— less subject to changes—than the outer layers. For example, the entities rarely need to be modified. Regarding the use cases, while it is true that they sometimes need to be updated, this should not be due to changes in the technologies used in the application, such as databases, frameworks, and libraries.

In summary, the Dependency Rule ensures that entities and use cases remain clean from any specific technology or external frameworks.

1.6 Inversion of Control

In a Clean Architecture, control that flows from outside to inside is implemented naturally, as it follows the Dependency Rule. For example, a method in an outer layer can create an object of a type defined in an inner layer and then call one of its methods.

However, in some scenarios, a use case needs to call a method implemented in an outer layer. To illustrate, suppose that a use case needs to send an email using a class, implemented in an outer layer, called MailServiceImpl with a send method:

public class MailServiceImpl {
  public void send(String msg) { ... }
}

However, this scenario implies a control flow from inside to outside. Specifically, the use case would need to declare a variable of a class defined in an outer layer, which violates the Dependency Rule.

Thus, the solution is to define an interface in the use case layer called MailServiceInterface with a send(String msg) method.

package UseCases;

public interface MailServiceInterface {
  void send(String msg);
}

// other classes

This interface serves as an abstraction for the mail service. In other words, it prevents the use case from relying on a concrete implementation of this service.

Furthermore, since MailServiceInterface is part of the Use Case layer, other classes in this layer can call send without violating the Dependency Rule.

The MailServiceImpl class should then implement the MailServiceInterface.

import UseCases.MailServiceInterface;

public class MailServiceImpl implements MailServiceInterface {
  public void send(String msg) {
    // calls external service to send an email
  }
}

This implementation does not violate the Dependency Rule, as a class from an external layer (MailServiceImpl) is using a code element defined in an internal layer. In this case, this element is an interface (MailServiceInterface).

The following class diagram illustrates the solution we have described.

Dependencies in a clean architecture

1.7 Conclusion

A Clean Architecture considers several concepts we studied in Chapter 5, including design properties such as cohesion, coupling, and separation of concerns, and design principles like Single Responsibility and Dependency Inversion. It also uses the Adapter design pattern, studied in Chapter 6.

In summary, the main recommendations of Clean Architecture are the following:

By following these recommendations, you will define an architecture that separates two types of interests (or requirements): business interests and technology interests. As a result, it will be easier to test your system and adapt it to new technologies that will emerge in the future.

More Information

If you wish to learn more about Clean Architecture, you can refer to the book of the same name by Robert C. Martin (Uncle Bob).

Exercises

1. In a Clean Architecture the name of an element declared in an external layer should not be mentioned by the code of an internal layer? What is the main benefit of this rule?

2. We intentionally did not mention the layer of the MailServiceImpl class in the article.

  1. If we intend to fully follow the Clean Architecture principles, why can’t MailServiceImpl be implemented in the Adapters layer?

  2. In which layer should we implement MailServiceImpl?

3. Assuming a Library System that uses a Clean Architecture, answer the questions below:

  1. In this system, there is a Book class. In which layer should it be implemented?

  2. The system also includes an interface that abstracts the database. For example, it defines a method that retrieves a list of books borrowed by a certain user. To which layer does this interface belong?

  3. There is also a class that implements the interface mentioned in the previous item. Specifically, this class uses several SQL queries. In which layer should it be implemented?

  4. Finally, in which layer is the database system (which can be MySQL, PostgreSQL, SQLite, etc.) located?

4. Suppose a system uses technologies X, Y, and Z, and we are certain that these technologies will never change in the future. In this scenario, do you think the adoption of a Clean Architecture is still beneficial? Justify your answer.

5. Under what circumstances might it not be worthwhile to use a Clean Architecture?


Check out the other articles on our site.