What are Microfrontends?

What are Microfrontends?

Coming from a microservices background, the first time I heard the term ‘Micro-frontend’, I was left scratching my head. I understood what microservices were, and how they interacted with each other via various protocols (sync/async) or via message brokers. But what could micro frontends be, and how would micro frontend applications interact with each other? Can they be technology agnostic like in the case of good old microservices? What would be the challenges of breaking a monolith UI application into a micro frontend architecture? How to implement micro frontends from a coding perspective? It took me a while to get my head around these questions. Through this article, I will try to summarize and share my learnings.

?What are Micro-frontends?

Micro-frontend application is basically one UI application containing sections or having routes that are pages of another application. Just like micro-services do it for the backend, micro-frontends break the traditional monolith frontend application into separate applications based on respective features. These separate applications are maintained as separate code repositories and can be worked upon by different engineering teams. Micro-frontends are technology agnostic, eg. one application may be written in React, and the other in Angular, and they will still come together to be part of the main application hosting these micro-frontends.

The micro-frontend architecture consists of one?Container/Host?application and one or more?Remote?applications:

The container application is the main application that integrates with different remote applications to render different sections of the website. Note that these micro-frontends (remotes) do not have any direct communication with each other, and each integrates with the container application directly. There should be no coupling or sharing of state between the applications.

There are 3 types of micro-frontend integrations:

  1. Build time integration?(aka Compile time integration): Sharing of libraries, eg. npm modules. The issue with this approach is that the container app has to be redeployed if there is any change in the dependent module. Plus, there is a tendency to tightly couple the container app and the module library. Not a true micro frontend architecture.
  2. Run time integration?(aka client-side integration): After the container gets loaded into the browser, it gets access to components from the integrated micro-frontend apps. While this approach solves problems with option 1 above, the tooling and setup is far more complicated.
  3. Server-side integration: While sending down JS to load up a container, the server decides whether or not to include other application components. The third approach requires a lot of backend code, and possibly the use of reverse proxy servers, which again are a pain to set up for local development.

Out of all the above, option 2 above is the most effective and flexible option for implementing true micro-frontends, and that is what we are going to cover in this article. To achieve runtime micro-frontend integration, the easiest and the more traditional approach is to use iFrames in the container app. But iFrames come with many issues: isolated (can’t share any dependency or a feature), poor performance, and difficult to get a good UX.

The better and more modern way to achieve runtime micro-frontend integration is by using the plugins approach.

Webpack provides WebpackFederationModule?that helps implement micro-frontend applications.

Some challenges when using micro frontends:

  1. Sharing dependencies: If 2 remote apps are using the same dependency but with different versions, the container app will end up loading both. And often this is the required behaviour as both components of the remote apps might be working with their respective version of the dependency. But to reduce the size of the container app, there is a trick so that all remote apps end up sharing the same dependency version. The trick is to expose the shared dependencies in the?ModuleFederationPlugin?configuration. You can also choose to expose entire package.json dependencies against the shared key for the Webpack plugin config. The container app will load the dependency 1 time, and the other remote apps shall reuse the same copy. But for this to work with remote apps, the latter also needs to load the index.js file asynchronously so that Webpack gets the opportunity to locate a shared dependency and use a pre-loaded copy of the same. But sharing of dependencies works only if they have the same major version. Eg. "terser": "^5.15.0", and "terser": "^5.16.1" in different remote apps would end up sharing the same version of terser, but if it was something like "^6.16.1”, then different copies will get loaded in container application to support the different versions of remote apps. But there can be some dependencies, like react, which can’t be loaded in a browser more than once. To restrict a dependency to be loaded as a single version copy only, add?singleton: true?inside the?shared?key of the Webpack configuration of the app. Now if you try to load more than 1, the app will break and an error will show up in the console.
  2. CSS:?A CSS style loaded from a remote app, can impact the CSS of the container or other loaded remote apps. Eg. If you have an HTML element <h1> in a container app with some styling, but a remote app has a <h1> with a different styling, once the remote app is loaded, the styling changes for the parent app's <h1> element as well, and this may not be desirable. So how do we scope the CSS per micro frontend app? Use one of the options below:

2.1 Use css-in-js library, which generates random CSS tags per usage. Read more about it here:?https://medium.com/jobsity/css-in-javascript-with-jss-and-react-54cdd2720222

2.2 When using a framework like Angular/Vue, use the framework’s component style scoping.

2.3 Namespace all your CSS. Ie in CSS file give as: .remote1 .h1 {…}, and in the container HTML use the remote as <div class=”remote1”><h1>……</h1></div>. But now care has to be taken by developers to namespace all CSS in the remote app, and also this is kind of creating a kind of coupling between the remote and container app, hence not a preferred solution.

3. Authentication: How to manage the logged-in user’s information across various applications? The best implementation is for the container app to maintain user AuthN details, and pass them down to all remote apps as and when they are routed to. The webpage for authentication can be a separate remote application as well, but that would only be for the HMTL rendering of the authentication page, and not for managing the authentication centrally.

4. Path routing:?Maintaining a history of routes when migrating from a page of the container app to page(s) of the remote app. There are many ways to implement this routing. The easiest way is for the routes in a container to point to the remote app, and the routes in the remote app to point to specific pages of the remote app. To maintain the history of pages visited, use 'Browser History' in the container application, and ‘Memory History’ in all remote applications. You will need to sync these histories between applications. Ie, if a route is navigated in the container app, we need to communicate that to remote apps to update their respective memory histories, and likewise, if a route is navigated wrt a remote app, then the same needs to be communicated to the parent app to update its browser history.?This communication is usually done using callbacks.?

Refer to this link for more understanding of browser history vs memory history.??


Concluding the article with a GitHub POC URL, where we run 3 different micro-frontend apps: 1 container and 2 remotes. Check the webpack.config.js of each application, on how different files from remote apps are exported, and then imported into the container app.

ramit21/micro-frontends: Micro frontends POC (github.com)

要查看或添加评论,请登录

Ramit Sharma的更多文章

  • AWS Certifications - difficulty ranking

    AWS Certifications - difficulty ranking

    Having taken a majority of AWS Associate/Speciality/Professional certifications, I rank all AWS exams in increasing…

    12 条评论
  • Reactive programming using Java

    Reactive programming using Java

    This article is about Reactive Programming, and how to achieve the same in Java. Reactive programming helps to create…

  • How to prepare for AWS certifications

    How to prepare for AWS certifications

    I keep getting messages on how to start preparations for AWS certification exams. So in this article, I will briefly…

  • Server vs Client-side load balancing

    Server vs Client-side load balancing

    This article discusses the key features of server-side and client-side load balancing. At the end of the post, there is…

    14 条评论
  • HTTP 3 is here

    HTTP 3 is here

    The demand for low latency web and mobile applications has brought significant evolution in HTTP protocol in recent…

  • Why 'REST' when you can 'gRPC'

    Why 'REST' when you can 'gRPC'

    gRPC stands for gRPC Remote Procedure Call. It's an open-source framework by Google for remote procedure calls based on…

    2 条评论

社区洞察

其他会员也浏览了