Monoliths in the shadow of the micro

Microservices are emerging nowadays for the simple reason that monolithic applications are complex to maintain, evolve and administer. It was therefore necessary to segment our monolith into a multitude of fragments. These fragments allow for a much simpler technical and/or functional evolution.
This evolution is now common in the backend domain. However, the problem of the monolith is not reserved exclusively for the backend, it can also be found in frontend technologies. SPAs have become more and more widespread with the help of frameworks such as Angular, React, or even Vue, but they remain monoliths. Although a multitude of tools within the frameworks allow us to improve the architecture and optimize the segmentation, the code remains mutualized. It is therefore necessary, to get as close as possible to microservices, to segment and evolve towards micro-frontends.


Microservices have appeared in backend architectures with the aim of segmenting the code into a multitude of autonomous and independent applications. This approach allows for improved scalability and maintainability of an application.
In the Frontend domain, practices are moving in the same direction with the use of modules and libraries that can be reused throughout the application. This segmentation has advantages on both a technical and organizational level. Indeed, this practice gives a team the possibility of managing a logical business from A to Z. From the management of the database, through the exposure of the data (Microservice), to the implementation of a graphic interface (Frontend). Thus the evolution of a particular functionality would be mastered by a single team that would be focused on the integration of that functionality and not on the integration of that functionality into a given architecture.
Currently, if we study all the solutions available on the market, we quickly come across the Web Components technologies with Webpack 5 and the use of “Module Federation”. These practices seem to be the most appropriate for sticking to a micro-frontend approach, both technically and in terms of user experience.

Evolution from Monolith to Microservices

As we can see today on our product lines, application development was done through a monolithic architecture which was composed of all the bricks of an application (the database access layer, the business logic, and the presentation).
As mentioned above, the architecture of an application has evolved or rather been segmented in order to optimize its maintenance and evolution. Figure 1 below highlights this evolution and guides our thinking for the Microfrontends.

Figure 1. Evolution des architecture applicatives

Monolith

All the bricks that make up an application are located in the same application. Access to the database, the business logic and the user interface are grouped together. During maintenance or development, all the bricks must be redeployed.

Architecture Backend / Frontend

The segmentation of the backend and the frontend appeared with the arrival of “xml http requests” which allowed the manipulation and communication of data using the http protocol. The frontend is now autonomous and the backend is still composed of business/technical logic and data access.

Architecture Microservice

The segmentation of the monolith on the backend with the use of Microservices has made it possible to define an application by business domain and thus to better distribute the loads on the development teams (one domain associated with one team, for example). We therefore find ourselves with a set of autonomous and independent applications capable of providing a single service associated with its business logic. With this type of architecture, we see the appearance of API Gateways enabling incoming requests to be redirected and secured.

And the monolith on the front end?

Finally, when we take a closer look at our Microservice architecture, we notice that this segmentation has only been done on the backend. Indeed, the backend benefits from a good segmentation into business domains, exposes an API accordingly and becomes completely independent. Nevertheless, the frontend application is still very consistent. The problem of the monolith has therefore only been partially resolved. This problem is all the more visible on large applications with a scalability that goes rather well on the backend but is more problematic on the frontend (obligation to take over and redeploy the whole application on the frontend).
Technical problems appear:

  • An evolution from a functionality point of view on the backend is equivalent to instantiating a new application (Microservice) but on the frontend, it is equivalent to adding a functionality among others. This means that we end up deploying an application that is more and more consequential (higher risk of crashing).
  • Migration is an integral part of our work today, it is necessary to think about future uses and the possibility of obsolescence of current technologies. The more compact and tightly linked our code/architecture is, the more complex the evolution will be.
  • The next issue follows directly from the previous one with the difficulty of finding developers in a few years when the technology will be obsolete.

As with the backend, we had to find architecture and a way to decouple our front-end application into several independent and reusable domains. This would guarantee better maintainability and integration into different applications.
The componentization allowed to partition functionalities that fulfilled one and the same function and this independently of the environment in which they were integrated. The development would be handled by a single development team responsible for the entire business chain (Microfrontend API). This brings us to another benefit, which is the ability to modularise an application according to the client. Indeed, if each business component is autonomous, independent, and can be integrated into any environment, it is quite possible to aggregate these components to make an application.

Figure 2. Evolution towards a micro-frontend architecture

Finally, from the user’s point of view, the application will be an aggregation of shared components, each managing a particular business domain. The “container” will therefore be the guarantor of the orchestration of all these components (see Figure 3).

Figure 3. Micro-frontend architecture from a UI perspective

Having the ability to split our frontend into several Microfrontends is leading us more and more towards a division of development teams by business domain. This would allow the team to be in charge of business logic from A to Z (from Microservice to Microfrontend). Finally, front-end application integrators will no longer need to know the Microservice domain inside out. They will only have to integrate the component created by the Microservice team.
When the Microservice evolves, the adapted component will be developed at the same time by the same team (but probably different technical experts) and will thus be ready to be integrated quickly by all the client applications.

Advantages and disadvantages

Although the core of the problem was the difficulty of evolution and maintenance within a monolithic frontend, it was also noticeable that in development, the teams at the end of the chain had to know all the Microservices with their constraints, restrictions etc…
Now the teams are organized around business domains and take charge of the end-to-end development of a service. The advantage is that they don’t have to worry about the environment in which the whole thing will be integrated. They can concentrate solely on their business areas. As for the integration teams, their sole purpose is to integrate an application with a multitude of Microfrontends. They no longer have to worry about business issues. Finally, in a more general way, it makes a team more specialized and avoids it being a generalist.
One of the disadvantages that can be noted with this type of architecture is the need for broader technical knowledge. Indeed, each business team will be responsible for the development of both the Microfrontend and the Microservice.

In practice…

Today’s web provides us with an enormous number of tools to create our applications. However, the choice of a technology or a framework is mainly based on the need, the delivery time, or even the budget. When we talk about Microfrontend, we distinguish two categories of solutions: applications that will share the same execution environment and those that do not.
The vast majority of Frontend applications that are developed today are applications that give a shared execution environment in order to guarantee the best result for the developer but also for the user.

Separate execution environment

Applications following this pattern have the possibility to guarantee a clear and precise separation from the rest of the application. Although it is beneficial on the one hand, it has disadvantages, especially when it comes to communication with the outside world. Nevertheless, from the user’s point of view, the Microfrontend will appear as a brick (more or less integrated) of the application.

Iframes

This is probably the concept that is best known of all, Iframes have the ability to run one web application inside another. The user sees a single page but the technical separation is there, too much so.
This tool has, as previously mentioned, the capacity to make several pages/applications cohabit but brings a lot of disadvantages. The communication of data within an application is an important criterion for an interface to be as “user friendly” as possible. In the case of an iFrame, inter-frame communication is more than limited and the integration of several Iframes remains superficial from a user’s point of view. The last disadvantage that could be the major one is the management of CORS which becomes complex.
Finally, the iFrame is a tool that is rather old-fashioned nowadays and that answers old problems. This approach is used for a purely static integration of a page, completely sealed from its execution environment.

Micro Apps

In the case of micro apps, the separation is less clear-cut than for iFrames, but remains present with the use of application routing to serve one Microfrontend or another. In this case, we are approaching a segmentation by business domain. Example:

However, this practice implies the implementation of a hub allowing the user to be proposed and redirected to the right Microfrontend. This makes it possible to get as close as possible to microservices with segmentation by business domain but requires good management of identity transfer (authentication, preferences, etc.). Moreover, communication between the micro-frontends and their mutual knowledge remains complicated.
Although this solution comes closest to our vision of micro-frontends, it is far from satisfactory today. Not least from a user point of view, who will suffer from a somewhat confusing user experience.

Shared runtime environment

When our application offers a shared execution environment, all resources are available to all components of the application. We then speak of Microfrontend as components of varying size that are integrated into the same application. This segmentation by component is as visible on the user side as it is on the technical side.

Web components

Web Components are blocks of code that encompass the internal structure of HTML elements, including CSS and JavaScript, and allow the code concerned to be used in the desired place in websites and web applications.
These can be integrated into an application in the same way as the current blocks offered by HTML (div, input, etc.). With Web components, we have the ability to create HTML/CSS/JS bricks that fulfill a precise function and can be integrated into any application.
The web is nowadays a field that evolves very quickly, new frameworks and libraries are released every month and it is difficult to make a choice in this mass of tools. Web components are coming on the market to guarantee the durability of our development with JS/CSS encapsulation and W3C support. Communication between components is simplified by the “events” system. The idea here is not to detail the functioning of the web components but the harmony between the web components is governed by a “base app” which orchestrates everything.
We have a real solution that is sustainable and allows us to optimize our application. However, a micro-frontend cannot be reduced to a component. It is associated with a business domain and therefore a business logic, REST calls, data transformations, several interfaces, etc. It would be reductive to say that a micro-frontend is a component. This problem has been solved by javascript frameworks.

Framework Javascript

Javascript frameworks allow us to create more complex components with services, integrated into a module that are themselves associated with a business domain. We can quote for example Angular, React, or VueJS for the most known. These frameworks allow us to segment our application into a multitude of modules corresponding to the business domains of microservices.
Note that modern frameworks use Web Components technology. For the sharing of complex components, this is the most efficient solution, both at the development level and at the UX level. It is possible to integrate different modules transparently for the user and to manage workflows in order to guide him in his tasks.
As an indication, it is possible to make several SPAs from different frameworks cohabit using a tool such as “SingleSpa”.

Webpack 5 : Module Federation

Webpack is a well-established static module bundler for modern JavaScript applications. It has over 57,000 stars on GitHub and over 13 million weekly downloads from the npm registry. It released version 5 on 10 October 2020, which brings a number of changes including a new feature: Module Federation.
Module Federation allows multiple web components (built using webpack) to work together. An application can dynamically execute code from another bundle or build. This is a revival for the world of micro-frontends.
Each webpack build can be a host, which is a container for loading other builds. It can also be seen as a remote, which is a micro-frontend to load. Each application can be a remote and a host, consumable, and consumer of any other federated module in the system.
Furthermore, Module Federation does not necessarily need to load the main entry point or an entire other application. It only needs to load the necessary code, which is a few kilobytes of code.
Thus each application has the possibility to work autonomously and independently because it is first and foremost a full-fledged application but it is also able to be packaged and integrated into any other application.
This approach makes it possible to reference parts of the program that are not yet known at the time of compilation. These can be self-compiled micro-frontends. Furthermore, one of the important points of Module Federation is the ability to share libraries between the different micro-frontends. This considerably reduces the size of our bundles and our entire application.

Application

The shell of our application can be seen as the entry point or orchestrator of all the micro-frontends embedded in our application. It is ultimately the host and the micro-frontends the remotes. Module Federation allows (Figure 4) to share libraries between several micro-frontends. We can distinguish four Angular applications sharing the same stack of libraries, including Angular.
With this approach, we can see that it is easy to make several micro-frontends from different frameworks cohabit. Indeed, Webpack5 takes care of packaging our different remotes in Web components, which makes their use much simpler.
Namely, a remote can also be a host by hosting other micro-frontends (Figure 4).
In the figure below, we can see a routing mechanism allowing the various micro-frontends to be loaded. Here again, it is possible to load them dynamically with lazy loading in order to reduce the bundle as much as possible when the page is loaded.

Figure 4. Logic of micro-frontends according to Webpack 5 Module federation

Webpack 5 allows us to clearly propose a solution for micro-frontends by coupling Web components to library sharing. Note that Angular or React are javascript frameworks that already integrate Webpack in their packaging pipeline. Module Federation is therefore a feature available in these two frameworks.

Conclusion

With JavaScript frameworks such as React and Angular, developers can define reusable web elements. However, these frameworks use another standard that in many cases prevents the reuse of practical code snippets in different projects. Web Components, reusable HTML components that are independent of the framework, help to remedy the situation.
However, as we have seen, the use of raw web components does not allow us to propose complex components with a real business logic behind them. The arrival of Webpack5 with functionalities such as Module Federation makes it possible to couple Web components and the use of javascript frameworks.
Finally, the best granularity than current tools can offer to have a micro-frontend architecture is the use of javascript frameworks coupled with Webpack5 in order to better manage the use of Web components.

More ...

Retour en haut