top of page
  • Writer's picturePratik Bhattacharya

State Management II - World of Micro Frontends

This blog is the second part of the State Management series. In the last blog, we talked about State Management as a concept and why state management is necessary for a frontend application. In this blog, we shall discuss state management in a micro-front styled architected application.


Pre-Requisites

Some knowledge about the following topic is necessary for you to understand some of the key terms in this blog

Let's talk Micro Frontends 🤷‍♂️


Micro frontend in itself is a separate topic and needs its blog. However, for this blog, we shall try to surmise it in a few words. The concept of micro frontend came from the concept of Micro Services, and Martin Fowler coined the term. The idea is to slice a monolith frontend into smaller chunks that can be managed, developed and deployed independently by smaller dev teams.


Let's talk about a Banking customer portal. As a customer, when you log in to your bank's e-portal, you can perform various operations like Check Balance, Create Fixed Deposits, update your phone, etc. In a modern enterprise setting, each of these domains should be a separate bounded context. Each is deployed in isolated services, with a standard interface for inter-communication; in other words, they are Micro Services. Although the services and APIs are hosted in a Micro-Service fashion, the customer will still need to see a unified experience in the UI portal. In a more traditional setting, the frontend would have been architected in a 'monolith' fashion, where multiple pages would have been developed under the same project. Similar to any monolith systems problems such as maintainability, accidental code changes, unintended coupling, extensive bash deployment, regression exists in such a setting.




Micro frontend architecture helps us tackle this problem by slicing the monolith into several components, with multiple teams working on each piece. These individual pieces can have their dev team, unique deployment cycles, and even their UI framework.

Find the sweet spot 🍬


State Management is critical to the long term success of your frontend application, and managing state in a micro frontend architected application is tough.

A common approach for managing state (using standard libraries like Redux) would be to have a global state encompassing all the micro frontends simply. Having a shared global state eases some of the concerns (like communication, subscription, etc.); however, it introduces an unwanted coupling that we wanted to remove in the first place. Drawing an analogy with Micro Services, having a shared state is like having a shared database among all the microservices. Let's say you use Redux of managing the global state; then, as per Redux norms, you will need to have a common big Reducer to manage actions for the Global Store. This implies that you will need to know all the actions and keep all the handling logic in one place. Thus each micro frontend gets dependent on this common piece, which increases inter-module coupling due to this central dependency.





Hence it makes sense for each self-contained micro frontend to maintain its store. Although this allows the applications to remain isolated, it introduces a new problem. How will these self-contained micro frontends communicate with each other? Let's say both retail banking and investment banking needs to show a notification to the user. How will the Shell application (let's assume the shell application is another isolated micro frontend responsible for showing notifications) coordinate this information? To further complicate matters, these notifications are actionable (clickable), and the parent micro frontend needs to react to the user clicks. So we have 3 self-contained, isolated applications, but they need to have a line of communication for a common process.

Apps in a Micro Frontend setting need not be aware of the presence of other apps and their states but must have a standard way of interacting with each other. So we need to find that sweet spot 🍬 between isolation and inter-communication.


Virtual Global State 🌐


Now there are multiple ways of solving this problem, and the solution depends on how complicated a form of communication is required. The solution that I am about to describe is a tried-and-tested mechanism applied in a real-world micro frontend styled application in our org.

The simplest way of solving the problem would be to send simple events (custom events in JS). Although this is a good idea for cross-app communication, some of the other complicated scenarios are not solved by simple events. Let's say your application wants to read the state of some other application, subscribe to changes made in some application, or send out a global message that multiple applications might need to react to. Simple events can't be applied to all such scenarios.

We solved this problem by creating a virtual global store amalgamation of various stores and then establishing a line of communication between these stores without dictating terms. These individual stores don't even need to know about the existence of the other stores.


Behind the Scenes 🎭


Read

In our framework, each individual micro frontend can maintain its store, which will contain the necessary actions and reducers required by these particular apps. These applications will now have the option to register the store with the shell framework. The framework shall maintain the mapping between each application and its registered store. The framework then exposes methods that will allow an application to read the state of another application's store. The framework also exposes other creative methods which will collect the state of all registered stores and present it as the Global State of the platform.


Subscribe

Similar to read, individual applications can attach a callback to other app's states. The framework will attach the callback as a listener to the required store, thus allowing an application to react to changes in another application's state. Like the global state, the framework also will enable applications to attach a callback to the global state; thus, applications can listen to change made in any registered state.





Dispatch

So far, I have covered how to read and react to other applications' states; we still need to solve the inter-app communication scenario. A state change in a flux-type architected application happens via actions. In this case, it's not wise to allow other applications to dispatch actions into other application's stores. This would lead to multiple issues, including security breaches, accidental updates, duplicate action names, etc. We deal with this problem by introducing a specific type of action called 'Global Action', which means that such actions can be dispatched by any of the applications in the ecosystem. The ownership and define a global action lies with the individual apps. As an app owner, you will have the freedom to declare specific actions as 'Global' and register these actions with the framework. You will also have the responsibility to share the contract of these global actions with other apps. The framework maintains a list of global actions for each store and exposes a method for other apps to dispatch the Global Actions. Based on the action type, the framework will dispatch the action to the registered store. The logic to handle the action remains the individual app store.


In Action 🎬


The concept that I talked about has been packaged as a NPM package, and the source code can be found in GitHub. The complete details about using the library can be found in our official documentation.

We are using this library in one of our internal platforms for managing the state in our organisation. The platform is a UI integration platform that has been architected using the core concepts of Micro Frontends.

To more about the framework, you can get in touch with us via the GitHub project.

6,566 views0 comments

Recent Posts

See All
bottom of page