Conf42 JavaScript 2022 - Online

Platform freedom with Micro-frontends

Video size:

Abstract

This talk will introduce the audience to an exciting framework that leverages Micro-frontend strategy and makes Typescript/Javascript applications available across different platforms like Web, Desktop, VSCode, Chrome extension etc. The framework is highly flexible and scalable for code development. Additionally, I will elaborate on the challenges we at Red Hat overcame by implementing this Multiplying Architecture that benefited our community and the end users to a great extent.

I’ll illustrate via some examples and implementation options and discuss Backend for Frontend(BFF), sync and async services, event bus, federated modules, and other opportunities for decoupling your front-end architecture.

Summary

  • Jamaica real time feedback into the behavior of your distributed systems and observing changes options errors in real time. This talk will take you through a list of microfrontend strategies. How this new framework helped in building an application that can run on various distributed systems.
  • Red Hat had a certain use case where we had to migrate one of our legacy product. We came up with a new framework on top of microfrontend. This multiplying architecture helped in solving some of the problems. The upcoming slides will take you through the list of learnings.
  • Web technology is totally dominating the software development at this point. One can run the web components anywhere, wherever you have the browsers. These are the list of platforms that we were targeting for our use case.
  • A micro frontend is an architectural style where independently deliverable frontend applications are composed into a greater whole. The goal of microfrontends is to decouple your monolith application. Each microfrontend can be accessed or developed by a separate autonomous team.
  • Micro frontend is connected to its own BFF, and that BFF connects to the microservice. There is no direct communication from the micro frontend to the back end. All the communication happens between via the BFF.
  • There are two main strategies that exist. One is runtime integration. The other one is built time integration. Both have their own pros and cons. The downside of this application integration strategy is the setup time or setting up.
  • We wanted to abstract our web components and we wanted to present to the various platforms that we have. So that's when we introduced a new framework called the multiplying architecture. Channel, envelope, view and editor are the core components. abstraction is being considered as the utmost priority for our use case.
  • The main highlighted issue that we were facing across both the strategies are the duplication of libraries that we're loading. To overcome this issue, Federated modules came to our rescue. This gave us solution to share dependencies across the microfrontends. And this also allowed us to implement the runtime integration.

Transcript

This transcript was autogenerated. To make changes, submit a PR.
Jamaica real time feedback into the behavior of your distributed systems and observing changes options errors in real time allows you to not only experiment with confidence, but respond instantly to get things working again. Code hello and welcome everyone to the Talk platform freedom with microfrontend. So this talk will take you through a list of microfrontend strategies, a new framework that was built on top of microfrontend, and how this new framework helped in building an application that can run on various distributed systems. So before getting into the topic, let me introduce myself. I'm Saravana Balaji Srinivasan. I work as a senior software engineer at Red Hat with the role full stack developer mainly on JavaScript technologies. I work in the areas where we build tools for business automation products, serverless workflow specifications, et cetera. So yeah, that's about me getting back to the topic. So this talk I wish to deliver in such a way like sharing my experience and the learnings we at Red Hat had while implementing microfrontend strategies for one of our use case. So to start with, maybe let me start with the red hat story. So sometime back we at Red Hat had a certain use case where we had to migrate one of our legacy product called JBPM, which is a business automation product. We had to migrate the product that can accommodate some of the latest technologies and it must accommodate the future technologies as well. So that's when we were thinking of microfrontend strategies. We had some sort of learning, sort of it, and then we came up with a new framework on top of microfrontend, which we named it as the multiplying architecture. And this multiplying architecture helped in solving some of the problems that we had. And finally it helped us to achieve the goal that we had. So the upcoming slides will take you through the list of learnings that we had out of the microfin ten and the new framework. And so yeah, that's the talk is going to be about. So firstly, I would like to break my topic, the platform freedom with microfin tens and so on. I would like to emphasize what I mean by the term platform here and the list of platforms that we were targeting for our use case. So we all know that web technology is totally dominating the software development at this point. So where the developer or the company who wish to build an application for their use case, web technology is considered as their default choice, right? So this is because web technology has stronger foundation, stronger standards, patterns, techniques, architectures, et cetera. And also web technologies are rich in ecosystem. So when we speak about ecosystem, we cannot ignore javascript, though it has initially started as a scripting language. Now we can find javascript everywhere. So over the period of time it has grown fastly. So that's the power of JavaScript at this point. And after the introduction of typescript, people like me who started a career as a backend developer on Java technology started liking typescript after learning started liking web technology after learning typescript because my code is totally type safe, right? And also I could emphasize that browser is everywhere at this point. You can find browser on your laptop, mission, mobile servers, et cetera, right? So now browser is just not only a tool to access your Internet or to access your guis, it has become more than that. So when I say browser is everywhere, one can run the web components anywhere, wherever you have the browsers, right? So these are the list of platforms that we were targeting. We wanted our web components to run on visual studio code as an extension and browser as an extension. Say for example, if you have GitHub, sorry, Chrome as a browser, my web component must run as a chrome extension, and in case of Firefox, it must run as a Firefox extension. And my same set of web components must run on GitHub as well as an extension. And finally we had web application as an option as well. So the reason for choosing these platforms is that the web components that we had were tooling components. These tools are going to be used by the developers who are going to build an application out of the business products that we have. And these are going to mainly used by the developers. And developers mostly use these four platforms, right? So that's why we were targeting these main four platforms. So while having this use case in mind, we were thinking about various architecture that were existing, starting from evolutionary architecture, and then the microservice, which is pathbreaker on the back end world that allows breaking a bigger monolith back end application into a smaller microservices and without being dependent on each service, and then code the serverless, which is a buzzing word at this point, which is another way called as back end as a service. You don't have to own an entire server, you can just have your back end logics run as a function and you can just pay for the number of hits that your function gets. So you don't have to own an entire server. So then comes the microfrontend, which is something that we are going to discuss elaborately in this talk. So I don't want to go and answer the question, what are microfrontends? You might have gotten enough answers for this question in lot of talks, right? So you might have seen images like this with a lot, I mean tall pillars and that represents monolith application, blah blah blah, et cetera. So let me just conclude with the quote by Cam Jackson. A micro frontend is an architectural style where independently deliverable frontend applications are composed into a greater whole. Simply the goal of microfrontends is to decouple your monolith application. So when you have a monolith chunk of application which is tightly coupled, and you work with one big vast team, you are obviously bound to lot of issues, bugs, and addressing all these bugs and issues will be very difficult. And the downtime of an bonolith application is also quite high. So definitely this has to be broken down into smaller microfrontends. So there is an idiom in English where they say don't put all your eggs in one basket. So definitely this applies for the microfrontend as well. You don't have to put all your code in one bigger chunk of monolithic application. You can diversify your application. So diversification is just not only applicable for your money, so it also applies for the technology as well. You can diversify your application into smaller microfrontends and you can make it independent. And each microfrontend can be accessed or developed by a separate autonomous team. So this diagram will give you an example of how microfrontend application looks like. So as I mentioned earlier in my previous slide, I work for a team where we build tools for various products. This is one such tool which is called as DMN, which handles business law rules, and it represents the business rules in a graphical manner. So considering this view, a header component can be broken down into a separate microfrontend. You may think here the header looks very small, why would that qualifies as a separate microfrontend? So decoupling a micro frontend or breaking your UI into a smaller micro frontend is definitely depends on the organization decision as well, or the team decision as well, whether you definitely want to break that particular component as a micro frontend or not. But on the other side, consider if you have an ecommerce kind of an application where header itself may contain a huge chunk of react components or any framework, right? So starting with your profile section, your company logo, search bar, drop downs and sub drop downs, too many components, right? So this definitely qualifies for separate microfrontend and could be made in by a separate team. So coming back to this view here, so as I mentioned, this header couldn't qualify as a separate microfrontend and the editor at the bottom which represents the graph definitely could maintain as a separate microfrontend and could be maintained by a separate team. And there is a navigation bar at the left could be maintained as a separate microfrontend. All these microfrontend ends are contained inside one container application which takes the major decisions. So while speaking about the Maya microfin or speaking about decoupling, microfrontend is not the only option that we have to decouple a monolith application. There are other strategies or techniques as well. So one such as modular monolith where your front end could be broken down into smaller uis and libraries could be shared among this, but still these uis are tightly coupled among them contained inside one single application. And there is another strategy which is called as integrated application, though here the uis could be broken down into smaller applications and the libraries could be shared between the applications. But this follows a build time composition and finally we have the strategy that we are discussing which is microfrontend where the UI applications pages are broken down into smaller applications and this microfrontend end follows runtime composition. So we will see what this built time composition or runtime composition elaborately in the upcoming slides. So again, this slide will give you an idea how micro frontend looks like and how microservice looks like and how they are differs. So the first one shows you how a monolith application looks like. You have your front end, you have your back end, and that back end connected to the data source. The request from the frontend passes through the back end and that backend fetches the data from the data source and passes on to the front end. When it comes to the microservice, your back end is broken down into smaller microservices. And there comes another layer between the front end and microservices which is called as the gateway API which takes the decision of routing the request from the frontend to the corresponding microservice, which can handle that particular request. After the introduction of microfrontend, the frontend applications is broken down into smaller pieces and that communicates to the API and that API routes your request to the microservice. So we will see another interesting strategy of the micro frontend which is called as BFF. Yes, you heard me right. Actually it is not abbreviated as best friend forever, but it is a best friend for the micro frontend. The actual abbreviation for BFF is backend for frontend. So this BFF ensures seamless user interaction between the microservices and microservice and the micro frontend and the backend microservice irrespective of the platform, wherever it is running on. So if you closely look at this diagram, each micro frontend is connected to its own BFF, and that BFF connects to the microservice. So there is no direct communication from the micro frontend to the back end. So all the communication happens between via the BFF. And this BFF is responsible of handling the request and response. It also converts a response from the backend and it presents it to the microfrontend in such a way that it can understand. And if you closely look at this diagram and there is no communication between the microfrontend as well, all the communication between the microfrontend ends happens only inside the container, application only, or the way you can call it as app shell. So while discussing about the microfrontend strategies, yeah, you have your microfrontend ends created and you have your teams working on each of your microfrontends. They are independently working fine, but all the microfrontend has to be integrated on the container application and it has to be presented to the user, right? So while thinking of integration strategies, there are two main strategies that exist. One is runtime integration. The other one is built time integration. We have seen this makes in one of our previous slides as well. So let me start with this runtime integration. So imagine if a team a building a component c and they deploys it so that component could be accessed in this URL, the domain name thecomponentname js because this component is deployed as bundle whenever the user tries to access the website, the container application takes the whole control and it loads the component c and it will decide when this particular component has to be rendered. So this strategy is kind of widely used across the people or developers who implement micro frontend, but it has its own pros and cons. The pro is being the teams can deploy their components at any point, they don't have to depend on other teams. And the downside of this application integration strategy is the setup time or setting up. This integration strategy is far more complicated. So coming to the next integration strategy that we have, that is built time integration. So in this case, again, considering a scenario where a team a builds a component so that component is shipped as an NPM package, this strategy, this NPM registry comes as a boon for us to contain all our components as a package. So the other team who wants to use that particular component, they can install that particular component as a dependency to their micro front end and they can just use it again. This looks easy to set up and understand, but the downside of this strategy is every time when you implement there is a change in the package that you build, the entire application has to be rebuilt again. Or if you want to introduce new dependency or new package or new component, the entire application has to be rebuilt again. So that rebuild time is again quite high. So there were other concerns associated with the micro frontend strategies. One such thing is styling. Imagine if you have various micro frontend ends when you apply one particular style for micro frontend that could override the styles on the other micro frontend as well, when you unify all the microfrontends on one container application. Yes, of course this could be resolved by using iframes, where each microfrontend can be rendered on the browser inside an iframe. So that this iframe totally isolates your micro frontend. So that changes on, I mean, style changes on one micro frontend will not affect the other one. But there are other concerns with using of these iframes as well, being micro, I mean, iframes is not something new or something. So it's a very age old approach. And at this point, at this evolution of technology, I feel that nobody will prefer to use iframes. Definitely developers across would prefer to use or to render the microfrontend and see just inside a dev or any other tags that they wish. So that's when after coming across or thinking about all these concerns that we had across the microfrontend and strategies, we were thinking of coming up with a new architecture. So these were the requirements that we were thinking about. We had actually starting with the multiple distributions. So the web components that we have must run on various distributions, like it has to run on vs code as an extension, GitHub as an extension, browsers as an extension, and finally as a web app. And this has to happen with minimal code changes. And there should be a bridge to the future tech stacks as well. So in case at present I use react framework for my web components, in future if there are some other frameworks, for instance, if I have to use Vue, I must be able to easily change that framework as well. So my micro frontend strategies, Maya, must accommodate any new technology that I wish to migrate. So that's when we introduced a new framework called the multiplying architecture. So before understanding multiplying architecture, let me understand what is software architecture? So according to Ralph Johnson, a software architecture is about the important stuff, whatever that is. So what is that important stuff? So it is abstraction. So basically we wanted to abstract our web components and we wanted to present to the various platforms that we have. So abstraction is being considered as the utmost priority for our use case to migrate or to deploy our applications on the platforms that I demonstrated showed you in the previous slides. So these are the core elements, or the core components that we built as part of the multiplying architecture, one as being channel, envelope, view and editor. So the channel is being considered as the platforms that we were discussing so far. And view are obviously the web components that are built on react js and envelope is something that communicates, that enables a communication between the channel and view in order to pass data, et cetera. And editor, the editors that we were discussing, it could be the DMN editors that I showed in previous slide, or there were other few editors that we were using for products. So we wanted our editors or the tools to run on the web like this. So if you look at this screenshot, the BPMN editor is wrapped inside the envelope instance, that envelop instance is wrapped inside the channel. And the same editor must run the same set of web components, must run on the browser as an extension like this, on the GitHub as an extension like this, and finally vs. Code as an extension like this. So this is how the envelope enables the community between the channel and the view. So if you see here, there is no iframes users. So we are rendering our components of the micro frontend ends via div instead of iframes. And also these envelope, channel and editor are contained inside, I mean the envelope view and editors are contained inside the channel. So the advantage of envelope is the context isolation. So my microfrontend are totally isolated and there is a better communication established between the channel and the view. And this creates an autonomous team as well. And each team could have its own release cycles. And this entire micro, I mean, multiplying architecture are built on top of typescript, so it is totally type safe. So let's see, microfiber multiplying architecture in practice. So let me take you through the code base that I have. I'll be sharing this URL with you where you can access these examples. So here I have a folder called examples, which contains a list of microfrontends. I have some basic examples, like to do list ping pong example, and there's another called base 64 to PNG, which converts your image to a PNG. So these are individual microfrontends. And these microfrontends I wanted to run on various platforms. Here let me take the classical example of to do list so this is the micro, I mean to do list view is the micro frontend and I want this micro front end to be running on the channel vs code as an extension and it has to run inside the web application as well. So here if you see there is a web folder called which is again a different microfin and you will see a federated page for the to do list as well. So this web app containing all the examples that I have here to do list ping pong or base 64 et cetera. But at this point we'll just take one example and see how it is implemented. So firstly we'll start with how this multiplying architecture is implemented. So this multiplying architecture follows a separate, I mean dedicated folder structure. Let me start with the dependencies what are that you need to install. So firstly these are the two packages that you must use. So these are available as part of this key tools core. So at this point here I'm not mentioning the version here because the core implementing of this envelope is residing on the same repository. So I have used workspace here. But if you are trying this multiplying architecture you may see the exact release of the version number, the recent release of the version number that you can see on the NPM registry. So these two dependencies that you may need. One is envelope and envelope bus. These two enables the communication between the channel and the view. And if you go inside the SLC folder you may see three folders, predominantly main folders, that is API embedded and enveloped, starting with API. So this todo list API so this contains list of abstracted methods that are exposed to the external world and channel API file this file contains a list of abstracted methods which are exposed by the channel and consumed by the envelope. So using these methods, the communication between the channel and the envelope happens whatever the message that channel has to communicate to the envelope or happens inside these methods. So here this file contains only the abstracted method, but the definition of these methods are provided inside the channel. We will be seeing that as well. So there is again another file envelope API which contains an abstracted method which are exposed by the envelope and that are consumed by the channel. So there is another folder called embedded. So this is considered as a starting point that triggers the view. And if you see here you will see a react component embedded to do list envelope. So you will see what this embedded to do list envelope contains. So it is actually imported from this folder envelope which contains envelope to do list envelope TSX again which is a react component here, this view, I mean to do list envelope view contains your JSX code for the to do list application which contains all HTML content and the CSS content and the react code, all the use callbacks, use state, et cetera. Yeah, it is a functional component. Again, so this view is rendered inside the envelope, and this envelope is called inside the embedded to do list. So this embedded to do list envelope will be called inside the channel. So that is something we'll be seeing here. So now let's go to the channel vs code extension. So to implement, yeah, in our case we had to render our web components on the Vs code. So we were making some sort of configurations for the vs code extension. So if you are also trying the same, you may have to first learn how to create an extension on the vs code. So this folder structure, some of the files here deals with the configuration for the Vs code extension. So starting with this extension ts, which will initiate the extension and some of its dependencies as well. And if you see this index ts here, this todo list envelope view envelope is initiated here. This will trigger the react components that we saw in the multiplier. I mean micro front end, right? So if you see here in the package JSOn, the micro frontend is added as the dependency here. So let's take the easier example of web application. Maybe you will get an idea about how this really looks. So again, we'll show you how here the microfrontend end is added as a dependency here, installed as a dependency here. And I created a separate page for to do list which calls the embedded to do list which I showed in my other file. So this is imported from key tools example todo list view, which was the microfrontend that we were discussing so far. And this page is called inside the app TSX where I have provided various navigations because this web app contains their applications examples as well. So I've provided separate navigations for each of the examples that I have here. And all this API app is contained. I mean this is the root of the my react application. So let me start this application and see. So yeah, I'm starting this application on the close 9000 to load. It's coming up now. Yeah, so as I mentioned, so this web app contains a list of all the examples that I have in this repository. So let me go to this to do list view that we were discussing. Maybe I'll just create list conf 42 Javascript conference 2022. So I created this note, maybe I can just mark it as complete, or maybe I can unmark it. Also I can remove so just simple example of todo list. This is how it works. So the same set of micro frontend component or react components were used on vs code as an extension on the web app as an extension as well. So let me jump back to the slide. So so far we have seen the multiplying architecture in practice. So after implementing all this channel view envelope or multiplying architecture or microfrontend strategies, all these things still we were having some sort of issues while integrating all the microfrontend components into one container application, these were the issues that we were facing. So the main highlighted issue that we were facing across both the strategies are the duplication of libraries that we're loading. Say for example, if I have three microfrontends for my application, and each of the microfrontend has react as a dependency in its microfrontend. When I integrate all the microfrontends, three instances of react application react dependency is created. So if you just think about it's like duplicating of one dependency, though it is commonly used across the microfrontend, three instances are created. So imagine if you have more number of dependencies that are kind of duplicately created across the microfrontend, either the bundle size or the size of your application will become too high. To overcome this issue, Federated modules came to our rescue. So you might have heard about this term federated modules. This was introduced in the webpack in its fifth version. So this gave us solution to share dependencies across the microfrontends. And this also allowed us to implement the runtime integration as well. So here, this is how the webpack configuration looks like implementing the modules Federation. So initially the module Federated has to be imported from the package. And here on the plugins you must create an instance for the module Federation which must contain list of routes for the number of microfrontends that you have. And these routes will be rendered on the browser only when it is called, the particular microfrontend is called. And if you see here on the line number 23, the dependencies are shared among all these microfrontends so that there won't be duplicate instance of dependency also on the react side of it. So this is how each microfrontend will be rendered. On the view we are making use of the lazy imports from the react, we are importing the microfrontends only when it is needed. And here we have created routes and we are just navigating to the respective microfrontend and whenever it is called. So finally, after implementing the microfrontend I mean the multiplying architecture. With the microfrontend strategies, along with the federated modules, we were able to achieve these end results that we wanted, the multiple distributions with the minimal code changes, and also we were able to achieve the bridge between the tech stacks. So these were the achievements that we may make out of the multiplying architecture. We were able to implement the microservice architecture, and we were able to take the advantage of runtime integration, and we created the autonomous team. There was no duplication of libraries between the micro front ends. And yeah, we were able to develop a bridge between the text tags as well. So you can find the code base that I was showing here in this URL. In case. If you have any questions, please feel free to shoot it out.
...

Saravana Balaji Srinivasan

Software Engineer @ Red Hat

Saravana Balaji Srinivasan's LinkedIn account Saravana Balaji Srinivasan's twitter account



Awesome tech events for

Priority access to all content

Video hallway track

Community chat

Exclusive promotions and giveaways