Clean Architecture in Angular Applications

The way to build better Angular applications

Denis Hasanenko
The Startup
6 min readNov 29, 2020

--

Angular is a widely used framework. It has powerful mechanisms for creating enterprise applications out of the box, a large and active community, and of course its best practices.

I think everyone who uses Angular has read the official documentation and knows what it is the Feature, Core, and Shared modules. But as real practice shows, there is no silver bullet and it is impossible to create a tool that solves absolutely all tasks. We faced the same thing when we started developing our application several years ago.

The application structure proposed by the Angular team not only did not fit our application but in some moments even began to interfere. Of course, you can say that we simply did not understand how it should work, and perhaps at first it was, but after a while, it was decided to do something before it was too late.

Since Angular already has a quite powerful module support, it would be foolish not to take advantage of this. Since the project was already quite large at that time, it was easy to understand the data schemas and how to draw the boundaries between the modules. The next question was the structure within the module. And here clean architecture came to the rescue.

Clean architecture in a few words

As the term “clean architecture” was first mentioned by Robert Martin, aka Uncle Bob. In short, clean architecture is an approach to structuring an application architecture into layers by the areas of responsibility.

The idea of layers in architecture is not new enough and has already been tested by time and the community in real applications. But there are several nuances that need to be understood before we move on to an overview of the practice. Architectural approaches themselves are good for certain project sizes, so hybrid approaches are often encountered in real practice. Also, if we talk specifically about pure architecture as a pattern, it can be implemented in different ways and at different levels.

In general, the idea of a clean architecture is quite simple and shows the way of the responsibility division between the data model layer, data manipulation, and data display. It is important to note that each top layer does not know anything about the layers below the child, thereby achieving the same “purity”. Accordingly, the first and most natural approach to implementing this architecture is the application layer.

First steps

As I already mentioned, our application is quite large, and it would be strange not to use a modular approach, which is already well enough implemented in Angular. Accordingly, it was decided to implement a “clean architecture” at the module level.

The first and main criterion that was set was to make the project structure as flat as possible. Unfortunately at that time in the community, there was still no hint of successful or unsuccessful attempts to use Angular in this manner, but I always liked the idea of using the framework not as a constraint, but as a set of tools collected in one place.

It is also necessary to note that in the case of our project, we start from a fairly small number of basic entities, we can call them abstract “projects” or “products”, like any software project or product, but almost anything can be inside them. Just like real software projects differ from each other in approaches and development tools. Accordingly, to smooth out all this diversity, all possible types of projects and their interactions are configured with metadata. Accordingly, there are a lot of entities related to the project, and in particular, they may have connections between themselves.

And what we finally have?

After some time, we came to the very implementation that ideally suited our project. Of course, it differs slightly from the canonical pure architecture, but the goal was not in the unquestioning implementation of ideas, but in creating the most correct structure that solves our tasks. The structure of the directories is shown here and below will be a description of each of them.

Configurations

It is likely that this layer will be unnecessary for your project, but as I already mentioned in our project there is a lot of metaprogramming, therefore, in this folder, we store all the metadata, or in other words, the module configuration.

It also includes all the less fundamental things that relate to the configuration of entities such as enumerations, dictionaries, and so on.

Entities

Actually, this layer is responsible for the definition and logic of the data models. It can store both interfaces and model classes.

Repositories

For working with the API, we have a separate layer and data sampling occurs only at this level. This is necessary for several reasons.

Some data objects obtained from the API are subject to transformation, so in order to get the cleanest possible layer of Use Cases, we decided to do all these transformations aside.

The second and no less important thing is encapsulation. That is, this is done in order to hide the implementation of working with the API inside the module and not export it outside. Thus, we have solved for ourselves the problem of using repositories instead of services.

Services

This layer is directly related to the Use Cases layer, and it contains all the logic for working with data models. And yes, sometimes it’s just passing query results from the repositories.

Components

For really large applications, this layer can be useful for storing “clean” components, ie. having no internal interactions but mostly displaying data.

You can also omit the point with stateless components and use this layer for all possible components that will be reused in other parts of the system.

Views

It was also equally important for us to make this particular layer, which will be responsible for user interaction and the direct display of data in the form of final application pages. This is done more for aesthetic reasons in order not to mix components that will participate in routing with the rest and not to deepen the structure of the “Components” layer.

Conclusions

In our project, we have been using this structure for a long time and it has proven its viability in practice. Perhaps you will notice that the idea is not so far from the standard approach with Feature modules, but with some exceptions — we do not use anything at all except them, that is, we even excluded the possibility of using a shared module in the structure.

It is also possible that this structure will be quite redundant for small projects, so, as always, I recommend that you think carefully not only about the benefits of the application, but also about the potential problems that may arise.

I would be glad if this article is useful to you and do not hesitate to ask your questions, leave comments, and clap.

--

--