Book cover

Buy e-book on Leanpub

To report errors or typos, use this form.

Home | 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—aiming to achieve reusability, cohesion, technology independence, and testability. If we follow the classification used in Chapter 7, Clean Architecture can be considered as a layered architecture.

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

Clean Architecture

Next, we discuss each of the architecture layers.

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. Suppose, for example, a university with academic, financial, outreach, etc., systems. All these systems have classes such as Student, Instructor, Course, Department, etc. They are then called Entities. Besides data, entities can implement generic business rules. For example, a rule in our university defines that every Instructor must belong to exactly one Department.

Use Cases are classes that implement operations relevant to the system domain. For example, the system of our university should have use cases for creating a new class for a certain discipline, enrolling a student in a class, canceling a student’s enrollment, storing the subjects covered in a class, etc.

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).

Assume, for example, a system that uses a REST API for communication with the frontend components. In this system, the adapters should implement the API’s endpoints. That is, they must 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 to be sent to the frontend.

1.4 External Frameworks

In the most external layer, we have classes from libraries, frameworks, and external systems. For example, the databases are located in this layer as well as libraries for sending emails, accessing payment providers, communicating with particular hardware, etc.

In our example, the university may have a system for managing extension courses, which accepts payment via credit cards. For this, the system uses a third-party provider, which offers classes for payment processing. Therefore, such classes are 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, it is true that they sometimes need to be updated. However, this should not occur 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 are classes that should be clean from any 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 give an example, suppose that a use case needs to send an email. First of all, let’s assume that there is a class, implemented in an outer layer, called MailServiceImpl with a send method:

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

However, this example implies a control flow from inside to outside. Particularly, the use case must 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 works as an abstraction for the mail service. That is, it prevents the use case to rely on a concrete implementation of this service.

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

Finally, MailServiceImpl should 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 like 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. Thus, it will be easier to test your system and adapt it to new technologies that will appear in the future.

More Information

If you want to learn more about Clean Architecture, you can check the book of the same name by 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’s 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. It should be implemented in which layer?

  2. In this system, there is also an interface that abstracts the database. For example, it defines a method that retrieves a list of books borrowed by a certain user. This interface belongs to which layer?

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

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

4. Suppose that a system uses technologies X, Y, and Z. And suppose that we are sure that they will never change in the future. In this case, do you think the adoption of a Clean Architecture is still useful? Justify.

5. When is it not worth using a Clean Architecture?