Some months ago, I talk to you about how we use modern web frameworks within a Portal. I wrote about some ideas like orchestration and modularization to name a few. In this post I’d like to retake the same concepts, but I’m going to tell you how we are using them in a different approach. I dare say, in a more modern way. A micro-frontend one, even though I’m not extremely fond on that name. As a matter of fact, a portal could also be seen as a micro-frontend orchestrator.
First and foremost, what are micro-frontends? In short, it’s a way to divide your frontend in a series of theoretically independent elements, which are going to be developed also independently. You shouldn’t be confused with other frontend techniques that also divide the code in different fragments, fundamentally lazy loading as it is done, for example, in React. Some people will tell you that the main difference is that micro-frontends provide you with the tremendous advantage to develop each element with a different technology. From my point of view, the actual difference is the need to orchestrate. Obviously, you will load these micro-frontends lazily and you can also develop them with your preferred technology, but the main point is when, how and why they are going to be deployed on screen.
Hence, how can we achieve this orchestration? Unfortunately, there are no silver bullets. A solution which could fit me, probably won’t be the best answer in a different situation. Nevertheless, should you know how we tackle this problem, you’ll be able to understand challenges better and provide your own solutions.
In my opinion, building this architecture involves deciding how micro-frontends are going to be created, how they are going to be loaded, creating the main frontend application – the orchestrator – and, finally, solving a series of important issues that appear when you don’t have a monolithic frontend. Let’s see it.
As I told you previously, each micro-frontend is, in fact, an independent element. That means that it can have it owns dependencies and it could work on its own. However, that doesn’t mean chaos, you need to provide a common environment to make easier these services development.
In our case, we are using several techniques. Namely, a folder used as a common service-place for the project, yarn workspaces to avoid downloading each dependency hundredths of times a generator which helps us to create new services and a customized bundler focused not only on micro-frontend "transpilation" but also on preparing this micro-frontend to be used from the orchestrator engine.
To tell the truth, loading the micro-frontends is not related to deploy them on the screen, but to know the micro-frontends that exist within your application, and provide mechanism to read their code from the frontend orchestrator.
From my point of view, this is not a frontend task but a backend one’s. I mean to say, provided that we are developing a modular application, we can add new modules. Moreover, we are committed to avoid frontend orchestrator modification when adding these new services. Thereby, the knowledge of the different modules should be stored in a "server", a standard backend, database, serverless, whatsoever.
To tackle this challenge, we are applying complementary approaches. Particularly, we have a watchdog on folder structure to detect both new and modified services, which is also responsible for analysing service structure and deploying it on a database, a mechanism which allows code downloading from the frontend orchestrator and a security layer which ensures that the code can only be downloaded by authorized users.
Note that we need to expose internal micro-frontend code, at least a piece of it, as to be called from the orchestrator. This can be done using webpack capabilities by publishing transpiled code as a library.
Building a full orchestrator is complex. Just imagine, that you want to configure in a WYSIWYG approach where your elements are going to be shown on the screen, or if there are different layouts per route, to name a few. To solve this problem, we should go for a Portal. But in this case, I’m interested in a simpler solution, only a menu which provides access to the different micro-frontends. In this case, we only need to store the order of the elements and that could be done within the micro-frontend boundaries.
But, in this case, I wanted to go one step further. So, I decided to wrap this script and style loading in a Web Component. This approach is quite interesting, since it would allow us to use the Shadow DOM and to make simpler the service loading from the orchestrator point of view.
Take into account that when you use the Shadow DOM you could have problems with styling. But also, there are certain issues such as opening a dialog from the micro-frontend. In my opinion it’s worth giving it a try, but I prefer, for my use case, not shadowing the micro-frontend.
There are other challenges when taking this micro-frontend approach. Had I to select one of them I would pick up the following ones.