Microfronts. Learning from mistakes

In this article I will tell you what problems I have encountered when building microfront-ends, how they could have been avoided, and also a little to bring the experience gained into the rather rare topic of microfront-ends.







Microfronts on iframe



In one company, the CTO made a balanced and deliberate decision that microfronts should be on a par with microservices, and they should be served on iframes.



As an argument, by the way, the Office 360 โ€‹โ€‹product from Microsoft was given, previously it used `<iframe />` for the top and side menus. Now iframes are not there.



Reasons and prerequisites for microfront



One of the main advantages of microfront-ends is the division of the monolith's functionality into teams, the ability to conduct end-to-end tests more granularly, and also makes it easier to sell software in pieces (in the case of boxed solutions).



All available applications are React SPA. They have nothing in common except Material UI, React Router v4 and the embryo UI-kit as npm modules.



Some applications will be used and delivered both in a standalone version and as part of another application.



Microfronts were divided into large functional blocks:



  1. Application header. Routing between apps
  2. Dashboard application. With metrics and widgets. Each dashboard widget must be part of the corresponding application (connected via an iframe).
  3. The applications themselves. Some of them include parts of each other (but without fanaticism and recursion)






Thus, different release cycles are obtained, independent of each other. As long as the applications follow the internal API.



Problem # 0. Incorrect separation of microfronts



Unfortunately, microfronts have not been thought out deeply enough. An example is an online store. The buy button and shopping cart can be scattered in many places, but they are all one microfront. As a product card in 10 variations, and the ordering process (where bills and addresses). All of these can be separate microfronts with their own release cycles.







In reality, it turned out that applications were divided very roughly. In analogy with an online store, this is when one team makes the cart and checkout page, and the second team does the rest (including cart counters on all other pages). At the same time, everyone uses the same business logic, and the interface is reused at the level of the UI-kit, or the Material-UI library.



It turned out that functional applications (marked in green and purple) have a lot in common. A significant part of the business logic in these two applications had to be separated into a separate microfront and used. In fact, there are far from two such applications. In total, there were about seven functional applications that do not reuse logic at the proper level.



As a result, time savings on reusing applications failed. Duplication of functionality also remained at a high level. Using microfront-ends without iframes or components with more complex logic from the extended UI-kit could solve the problem of duplication of functionality.



Problem # 1. Lack of process orchestration



While a lot of questions about the organization of microservices were discussed. How they will communicate with each other, by what protocol, the organization of the process of interaction between microfronted was put on the back burner.



In order to neutralize all CORS problems once and for all, it was decided to plant nginx, which would handle routing. Thus, each microfront and each microservice had its own address, for example: The question remains, how to test applications during development mode? Will each application be served on a different port?



https://domain.zone/dashboard

https://domain.zone/header

https://domain.zone/app1

https://domain.zone/app2

https://domain.zone/api1

https://domain.zone/api2







This is where the `http-proxy-middleware` package comes to the rescue, which can be configured in conjunction with the CRA. It turned out that only half of the front-end developers were able to set up such a setup. Of course, one cannot blame anyone here, but such a problem has appeared, and it must be solved organizationally.



A clear versioning of all applications with a description of the functionality, available methods and internal API is also required. This leads to the next problem: documentation.



Problem # 2. Lack of internal API



For applications to interact with each other very well, documentation is needed. Unfortunately, in our case, only microservices have documentation. And the gaze did not fall on the microfronts.



This is a very critical part of the system in the case of distributed teams, and even with staff turnover.



It is also necessary to develop a mechanism for communication between applications. Here the `postMessage API` comes to the rescue, or direct access to another, built into almost every React application - Redux. What is not a message bus? But more on that later.



Problem # 3. Iframe is not flexible enough



There is nothing wrong with using the `<iframe />` tag. It is a powerful tool with built-in message bus (postMessage API) and extensive security customization.



However, in the case of microfrontations, `<iframe />` imposes a lot of restrictions. One of them is the inability to reuse one application in several parts of the page.



Reuse applications


In the case of an online store analogy, 10 Buy buttons will create 10 `<iframe />`, that is, 10 running React apps. There is not enough memory for this. This is one of the reasons why dividing the application into teams by features is not possible.



URL is not suitable as state management


We're all used to routing apps via URLs. This is also convenient when using the microfront as an independent unit. For example, when part of the main application is coherent enough to be useful on its own. This is, of course, not a unique advantage of the iframe approach, but it is quite simple to do.



Here is an example of how a purple application from KDPV with a different URL can work as a standalone application:







However, using the URL iframe interface to switch the state of the microfront in our case turned out to be impossible: the microfront starts loading from scratch due to incomplete integration of the history API with its `pushState `and React Router - get a full page refresh.



Outside สปiframe` click handlers


Imagine you are going to make a dropdown menu. As in the diagram above: from the pink menu. And also close it by clicking on an empty space. In the case of an iframe, you need to use the conditional postMessage API, since an out-click is simply not recognized due to different window objects. Or come up with a hack with a transparent background of the enlarged iframe element in full screen.



By the way, resizing the iframe and adjusting the parent application to it also becomes more cumbersome and complex.



Bonus Problem: Inappropriate Cookie Use



This problem is not directly related to microfronts, but it takes it to the next level of madness.



It was decided to write in the authorization cookies not only the token, but also the full list of rights to certain parts of the application. All this was encrypted by SHA - ??? and converted to base64.

As a result, the cookie size exceeded 8KB, which is the default value for nodejs / nginx, (or 2KB for the size of one Cookie record in Google Chrome), which led to more complex server configuration (without eject CRA, you can no longer start with this setting), and also to splitting this large encrypted dataset into smaller cookie strings.



This means that every request to the server, even to get `favicon.ico` or to get a list of available menu sections, is equipped with an additional header of impressive size.



Conclusion. How then to live with microfronts?



To begin with, of course, you need to decide on whether microfronts are needed? Often, a properly configured and enriched UI-kit in the form of an npm module solves the problem of both independent releases and the same visual style.



  • Don't use iframe. It does not simplify work, but only adds performance problems, severely limiting the ability to split the application into microfronts. Rendering chunks of SPA into specially reserved tags is a much more efficient solution.
  • Develop process orchestration: both in production and for development. Not every developer would want to dive into the related industry of routing and proxy when he was hired to rivet interfaces from ready-made blocks.
  • Develop a message bus between applications. This is much easier in the case of a single global space, the window object.
  • Create documentation on the interface for interaction of applications, as well as their launch and configuration.



All Articles