Published on

Centralized Redux Data Store for Angular and Vue Microfrontends

Authors

Centralized Redux Data Store for Angular and Vue Microfrontends

Title: Centralized Redux Data Store for Angular and Vue Microfrontends

Author: Nataly Chkhan

Subject: Module federation, state sharing (Angular & Vue)

Duration: 6 minutes read

Language: English

Link to repo: https://github.com/darklimeteam/module-federation-shared-redux

Why do I need state sharing for microfrontends?

Question about state management is a topic that is likely to come up in every front-end interview. Redux is probably going to be mentioned as well 🙂. If your company uses microfrontends, sharing data between applications would probably also be discussed. Why is sharing data important? Even if microfrontends are designed to be highly independent, there may still be some data that needs to be shared between them 🤫. State sharing is one of the biggest challenges faced by microfrontend architecture. This article aims to provide a step-by-step guide on how to share data between Angular and Vue microfrontends using the Redux federated module.

Project structure

The app, that was developed here as a sample, is about updating user's notifications settings.

Angular shell

When a notification option is changed in the Vue microfrontend, it triggers an update in the store microfrontend. Since the Angular shell has subscribed to the store data, it retrieves the updated value and displays it in the app component.

Why do we need a store module as a separate microfrontend🤔? In a complex application, having a single source of truth helps to avoid a tangled web of data flows between microfrontends. However, if an application is not particularly complex, a simpler approach to communication between microfrontends, as described here, may make more sense.

Let's go deeper into the project structure and elements. The app consists of the next microfrontends:

  1. Angular shell (Angular 14 application)
  2. SETTINGS federated module (Vue 3 module with a standalone component inside)
  3. STORE federated module (Redux vanillaJs module).

The Angular shell houses a wrapper for the SETTINGS component, as well as an App component that retrieves data from the STORE federated module. The SETTINGS federated module encompasses the standalone Vue SETTINGS component, which retrieves and updates data in the STORE federated module.

Project structure

There is the detailed overview of each module (microfrontend) below.

SETTINGS federated module

In the previous article, the Vue SETTINGS component was introduced as part of an Angular shell application. Here is a link to the article where the Vue SETTINGS component, along with the SETTINGS federated module were introduced. The Vue SETTINGS component is implemented using a custom Vue element and resides within the Shadow DOM to encapsulate its behavior. To effectively manage common data shared between microfrontend applications, an external store is the best approach in this case.

Now, let's take a look at the code for the standalone Vue component🍿.

import { defineCustomElement } from 'vue';
import { updateCurrentSettings } from "store/updateCurrentSettings";

const Settings = defineCustomElement({
    ...
  template:`
    <fieldset>
    <legend>Notification settings: {{selected == 0 ? 'Show notifications' : (
        selected == 1 ? 'Send notifications via email' : 'Turn off notifications')}}</legend>
    <div>
      <input type="radio" id="showN" value="0" v-model="selected">
      <label for="showN">Show notifications</label>
    </div>
    <div>
      <input type="radio" id="emailN" value="1" v-model="selected">
      <label for="emailN">Send notifications via email</label>
    </div>
    <div>
      <input type="radio" id="noN" value="2" v-model="selected">
      <label for="noN">Turn off notifications</label>
    </div>
    <button @click="onClick(selected)">Submit</button>
    </fieldset>
  `,
  data() {
    return {
      selected: 0
    };
  },
  mounted() {
    try {
      import("store/Store").then((val) => {
        this.selected =  val.default.getState().currentSettingsValue;
      });
    } catch {}
  },
  methods: {
    onClick(selected) {
      console.log(`Vue component: selected option - ${selected}.`);
      updateCurrentSettings(Number(selected));
    },
  },
  });

State management federated module

Our objective is to create a framework-agnostic federated module for data storage purposes only. Redux is a well-known state management pattern and widely used library for managing state, so this npm package was choosen for the module. This Redux npm package is tiny and can be used with React or with any other library. Let's explore how changes in the Vue SETTINGS component can trigger updates in the state management module. Here is the relevant code from Vue SETTINGS component.

import { updateCurrentSettings } from "store/updateCurrentSettings";
...
  methods: {
    onClick(selected) {
      console.log(`Vue component: selected option - ${selected}.`);
      updateCurrentSettings(Number(selected));
    },
  },

Function updateCurrentSettings is imported from the state management module. This function definition is listed below as well as appropriate Redux reducer.

const DEFAAULT_SETTINGS_VALUE = 1;

export function updateCurrentSettings(selectedOption) {
    bindUpdateSettings(selectedOption);
}

const bindUpdateSettings = (selectedOption) =>
    Store.dispatch(updateSettings(selectedOption));

function updateSettings(currentSettingsValue){
    return {
        type: 'UPDATE_SETTINGS',
        payload: currentSettingsValue,
    }
}

function reducer_SETTINGS_update(state = {currentSettingsValue: DEFAAULT_SETTINGS_VALUE}, action){
    switch(action.type){
        case 'UPDATE_SETTINGS':
            return {...state, currentSettingsValue: action.payload};
        default:
            return state;
    }
}

Redux devtools looks like this after submiting new settings value.

Redux

shared-store-redux module functionality can be checked separately ( http://localhost:3004/).

shared-store-redux module

To expose shared-store-redux module as federated we need to add ModuleFederationPlugin to the webpack.config.js.

    new ModuleFederationPlugin({
      name: 'store',
      filename: 'remoteEntry.js',
      library: { type: 'global', name: 'store' },
      exposes: {
        './Store': './src/store',
      },
      shared: {
        '@reduxjs': { singleton: false, eager: true }
      },
    }),

Angular shell

So the final step is to show SETTINGS data from state management module on the Angular shell component. Detailed explanation on how to configure ModuleFederationPlugin for both Vue and Angular modules listed here.

Repository with this sample

This article repo https://github.com/darklimeteam/module-federation-shared-redux.

If you consider microfrontend for your project these links and repos might be useful as well:

  1. Angular, React, and Vue in a Single Application.
  2. Using Multiple Frameworks in a Single Application with Module Federation in 2023
  3. https://github.com/darklimeteam/module-federation-angular-react-vue
  4. https://github.com/module-federation/module-federation-examples