Micro-Frontends Using Single-SPA Framework

Avatar img
Francisco Izurieta
Tech Lead de Payments @Kushki
junio 08, 2020
Lectura de 2 minutos
Micro-Frontends Using Single-SPA Framework

A micro-frontends architecture is a design approach in which a web application is broken down in individual, semi-independent micro parts that work together. This architecture can be as open as you wish, and each application can be implemented using different frameworks. The micro-frontend concept is widely inspired by microservices.

As for micro-frontends, it is important to note that even when an application is divided into several projects, each one of them will be integrated into a single one. Thus, for the final user, everything would seem to be a unique application, instead of many of them.

Although micro-frontends have started to receive lots of attention lately, there is not a single way of implementing them. In fact, there are a variety of approaches, depending on the objectives and requirements at each organization. At present, some of the best known implementations are:

  • Single-SPA Framework: This framework allows for the implementation of micro-frontends by combining other frameworks (React, Angular, Vue, etc). This is the implementation that we use at Kushki; we will explain it in more detail below.

  • IFrames: They isolate micro applications in iframes by using Window.postMessage APIs to coordinate multiple frames. IFrames share APIs exposed by the parent window.

  • Web Components: Front-end applications only deal with routing; they make decisions about the set of components to be displayed, and the orchestration of events among different web components.

Monolithic SPA versus Micro-Frontends

For Kushki's case, we will analyze the example of an application built with the purpose of allowing our customers to edit and view a variety of configurations available in our payment system, among other actions. This application was originally built as an SPA monolithic structure created in Angular 5. The application grew so much that, at some point, it contained more than 200,000 lines of code and about 13 rather complex components. All this resulted in an application that gradually became very difficult to maintain, and operating different features at the same time was an issue.

For this reason, we decided to change to micro-Frontends, in order to continue the development of this application. Several alternatives were analyzed, but the most mature alternative, with the best documentation, then and now, is the Single-SPA framework. In the beginning, we chose to migrate of the application while maintaining the framework with which the SPA monolithic structure was made (Angular).

In a first version of the new micro-frontend, it was decided that each of the original application components would become a micro-frontend. But, one of the difficulties that we found with this first version was the navigation bar, which had to be shared by all applications. Because of this, we decided to create an Angular library, which was installed in each micro-frontend. In this way, the first version of our first application with micro-frontends was released. It had the following structure:

Angular Library Micro-Frontends

Over time, we realized that our first idea, converting the navigation bar into an Angular library that has to be installed at each of our micro-frontends and updated for each application every time a change was made in the navigation, was not practical, and limited us to use only Angular for the development of our micro-frontends.

A Single-SPA framework requires the use of an index.html file, where all micro-frontends that are to be used by the application are registered. This file is called a root config file within the framework, and it usually contains a single HTML file where some framework methods are used to ensure that everything works correctly.

Then, to solve the problem of our navigation bar at Kushki, we decided that our framework root config file would not be a single HTML file, but rather a SPA built in Angular. We migrated all the logic that we had in the library to this one and configured the index.html library that Angular generates, to ensure that it works correctly with the configurations required by the framework (all this will be explained in more detail through an example below).

With this change, we managed to start developing micro-frontends, not only in Angular, but also in React. As a result, the original structure changed a little, and was established as follows:

Angular Library and Micro-frontends

When we abandoned our SPA monolithic structure and started using micro-frontends in our application, we managed to separate the multiple features we had in one place and optimized the introduction of new functionalities.

However, this does not mean that micro-frontends can be used for any project. For the vast majority of cases, a SPA monolithic structure is probably more than enough. A micro-frontends architecture is more suitable for large applications that group many functionalities. In Kushki, the vast majority of our SPAs are still monolithic structures, because they are not so big. This is why implementing this architecture must be considered only if a project is expected to have scalability problems over the medium- to long-term.

An Example of Micro-Frontends using a Single-SPA Framework

For this example, we are going to create a root application in React, where 2 applications created using a Single-SPA framework will be registered: one with Angular and one with React.

Part 1: Create a Root Config. Application

For the root application, only a web application created using create-react-app is needed. For this example, we are going to use npm as a javascript package administrator, and Typescript because it offers multiple benefits and it is now supported by React.

npx create-react-app root-config-app --template typescript --use-npm

Once our application has been created, we have to add single-spa to our project to be able to register our micro-frontends and browse through them.

npm i single-spa

The next step is editing the index.html file of the project to add the necessary scripts and imports to make our application work. Our file should look as follows:

<!DOCTYPE html>
<html lang="en">

  <head>
    <meta charset="utf-8" />
    <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <meta name="theme-color" content="#000000" />
    <meta name="description" content="Web site created using create-react-app" />
    <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
    <!--
        manifest.json provides metadata used when your web app is installed on a
        user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
      -->
    <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
    <!--
        Notice the use of %PUBLIC_URL% in the tags above.
        It will be replaced with the URL of the `public` folder during the build.
        Only files inside the `public` folder can be referenced from the HTML.

        Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
        work correctly both with client-side routing and a non-root public URL.
        Learn how to configure a non-root public URL by running `npm run build`.
      -->
    <title>React App</title>
    <meta name="importmap-type" content="systemjs-importmap">
    <script type="systemjs-importmap">
        {
          "imports": {
            "@single-spa-test/app-angular": "http://localhost:4200/main.js",
            "@single-spa-test/app-react": "http://localhost:8080/app-react.js"
          }
        }
    </script>
    <script type="systemjs-importmap"
      src="https://storage.googleapis.com/react.microfrontends.app/importmap.json"></script>
    <script src="https://cdn.jsdelivr.net/npm/systemjs@6.2.5/dist/system.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/systemjs@6.2.5/dist/extras/amd.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/systemjs@6.2.5/dist/extras/named-exports.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/systemjs@6.2.5/dist/extras/named-register.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/import-map-overrides/dist/import-map-overrides.js"></script>
    <script src="https://unpkg.com/zone.js"></script>
  </head>

  <body>
    <noscript>You need to enable JavaScript to run this app.</noscript>
    <div id="root"></div>
    <!--
        This HTML file is a template.
        If you open it directly in the browser, you will see an empty page.

        You can add webfonts, meta tags, or analytics to this file.
        The build step will place the bundled scripts into the <body> tag.

        To begin the development, run `npm start` or `yarn start`.
        To create a production bundle, use `npm run build` or `yarn build`.
      -->
  </body>

</html>

To avoid adding unnecessary CSS to our brief example, we are going to add Material IU to our project and add a quick navigation example.

npm i @material-ui/core

We add a new Nav component to our project, where we will copy the navigation example.

import React from 'react';
import { createStyles, Theme, makeStyles } from '@material-ui/core/styles';
import Drawer from '@material-ui/core/Drawer';
import AppBar from '@material-ui/core/AppBar';
import CssBaseline from '@material-ui/core/CssBaseline';
import Toolbar from '@material-ui/core/Toolbar';
import List from '@material-ui/core/List';
import Typography from '@material-ui/core/Typography';
import ListItem from '@material-ui/core/ListItem';
import ListItemText from '@material-ui/core/ListItemText';

const drawerWidth = 240;

const useStyles = makeStyles((theme: Theme) =>
  createStyles({
    root: {
      display: 'flex',
    },
    appBar: {
      zIndex: theme.zIndex.drawer + 1,
    },
    drawer: {
      width: drawerWidth,
      flexShrink: 0,
    },
    drawerPaper: {
      width: drawerWidth,
    },
    drawerContainer: {
      overflow: 'auto',
    },
    content: {
      flexGrow: 1,
      padding: theme.spacing(3),
    },
  }),
);

export default function ClippedDrawer() {
  const classes = useStyles();

  return (
    <div className={classes.root}>
      <CssBaseline />
      <AppBar position="fixed" className={classes.appBar}>
        <Toolbar>
          <Typography variant="h6" noWrap>
            Single-SPA Root Config
          </Typography>
        </Toolbar>
      </AppBar>
      <Drawer
        className={classes.drawer}
        variant="permanent"
        classes={{
          paper: classes.drawerPaper,
        }}
      >
        <Toolbar />
        <div className={classes.drawerContainer}>
          <List>
            <ListItem button>
              <ListItemText primary="App Angular" />
            </ListItem>
            <ListItem button>
              <ListItemText primary="App React" />
            </ListItem>
          </List>
        </div>
      </Drawer>
      <main className={classes.content}>
        <Toolbar />
        <div id="single-spa-application:app-angular"></div>
        <div id="single-spa-application:app-react"></div>
      </main>
    </div>
  );
}

We edit the App.tsx file to include our new component. It should look as follows:

import React from 'react';
import Nav from './Nav';

function App() {
  return (
    <React.Fragment>
      <Nav />
    </React.Fragment>
  );
}

export default App;

Once we have has done all this, we obtain a application as the following:

Single SPA Root

Part 2: Creating Single-SPA Micro-frontends

Angular

To create an Angular application using single-SPA, we'll simply create an SPA using the CLI provided by the framework. It is important to create the application using an Angular router, so that the micro-frontend works correctly.

ng new app-angular

Once the application has been created, we have to install the Single-SPA framework in our project. To do this, we must follow the instructions that we find in the framework documentation.

ng add single-spa-angular

This command automatically configures the framework in our application. We also have the possibility of installing manually (you can find the details in the documentation mentioned above).

Finally, we will create a simple component that we are going to show in this application and configure the routes that will be used by this SPA.

ng g c angular

In the route file, a main route must be configured, which must coincide with the route that is registered in the single-spa of the root config. It is also important to configure the Routing Module providers. The file should be configured as follows:

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { APP_BASE_HREF } from "@angular/common";
import { AngularComponent } from './angular/angular.component';

const routes: Routes = [
  {
    path: "angular",
    component: AngularComponent,
  }
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule],
  providers: [{ provide: APP_BASE_HREF, useValue: "/" }]
})
export class AppRoutingModule { }

That is all that we have to do when creating a micro-frontend using this framework. Once you've followed these simple steps, you'll be able to edit your Angular application as you wish.

React

To create a micro-frontend using React it is necessary to use the manager provided by the Single-SPA framework (create-single-spa). We should enter React when when the manager asks which framework to use.

npx create-single-spa

And, with React, this is all we have to configure.

Part 3: Register Micro-Frontends using Single-SPA

Once we've created the micro-frontends, we have to register them in the root config application that we created in Part 1. To do this, we must edit the index.tsx file as follows:

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import { LifeCycles, registerApplication, start } from "single-spa";

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById('root')
);

registerApplication({
  name: "app-angular",
  app: (): Promise<LifeCycles> => (window as any).System.import("http://localhost:4200/main.js"),
  activeWhen: "/angular"
});

registerApplication({
  name: "app-react",
  app: (): Promise<LifeCycles> => (window as any).System.import("@single-spa-test/app-react"),
  activeWhen: "/react"
});

start();

To be able to execute this example, you must run the following commands:

npm start # root config app
npm run serve:single-spa # angular app
npm start # react app

With all the applications running, we will have a completely functional micro-frontends example, developed in different frameworks, and in very few steps.

Single SPA Root Config


You can find the full code of the example we explained in this article in the following repository:

Micro frontends single spa example

Be the life of the party with the latest information on digital payments.

Subscribe to our Kushki Hub to receive alerts about our new content.

Suscribe illustration
Don't know which product is right for your business?
Does the world of payments catch your attention?

More about our kushki Hub

Reflections on Payment Channels that 2020 Has Brought for us

The payment methods that 2020 brings us Although we currently are in an information world in which we are exposed every day to extreme points of view, which make our emotions oscillate between the worrying and the amusing, the cultural and the hopeful, the following paragraphs were written to share some reflections regarding the evolution and perspectives of electronic payments in Latin America. Closing 2019, Same Shapes At the end of last year, the adoption of digital payments in Latin America did not show significant advances. Although the growth of electronic commerce in the region has been increasing with double figures for the last 3 years (20% increase in annual sales, between 2017 and 2019), digital payments have been mainly adopted by bank users. However, we now know that we were about to see a change in the way in which we made our payments. A Life with Adjustments We already know the story that 2020 has started to tell us, which demands from us a change in our daily life habits, and covers a variety of areas that involve both work and personal matters, including behavioral patterns, mainly related to our consumption habits. First Changes Faced with a restricted mobility environment, the first aspect that we have had to modify is the way we buy. Apart from visiting local supermarkets complying the health protocols that each country has established, we have had to adapt ourselves to buy many items online. This behavior might not have been so common for many of us, but it has become the forced solution to this situation. This is apparent when we see all those who prepared themselves for confinement by buying equipment for exercising at home, those who built up a medical arsenal, those who filled the stock with pet food, or those who pampered themselves by supplementing their pantry with everything they needed to cook the type of desserts or dishes that give us an immediate reward, and of course, we cannot forget those who went to the section of spirit drinks. But as some products have caught all our attention, others have lost their appeal at this moment, and they are all related to what the current circumstances are limiting us from enjoying. In this way, purchases of travel items, car accessories, and swimwear, as well as party supplies, mountaineering gear, etc. Second Changes After provisioning ourselves with the necessary resources to face this confinement in the best way, we have all the obligations that every household requires our devoted attention: paying primary services, bank cards, loans, taxes, governmental formalities, etc. Before face-to-face restrictions, we have had to look for ways to pay online to be able to cope with our responsibilities to this respect. For those who were already familiar with this trend, they are doing fine. For those who were not prepared, they have had to look for some family member or friend to teach them, or learn by themselves how to make these payments. Forced Necessity All businesses or private or public service providers has faced this urgent need to sell or collect resources through electronic media. Every day, companies are creating online platforms, and governments have had to modify laws and regulations to enable payments through this way. Reality is plain. Electronic payments are no longer an supplementary option or a test channel for selling or money collection, they have simply become the most viable option for the vital interaction between providers and users. Is Currency Dematerialization Real? Although this scenario shows that, in the long term, we will go towards this trend, the reality of our countries also show us that there are still many challenges to face. In view of the low bancarization, the high informality of money habits, and most users' mistrust of these remote payment methods, the road will not be followed immediately. This is why today some alternatives between these two worlds (Digital vs. Cash) are gaining much ground in our markets. Online payments made in cash in supermarket chains, convenience stores, and pharmacies, are growing at the same speed (a double-digit annually) than the adoption of e-commerce in the region. This is why we are going to see a gradual mutation in our payment habits as we advance towards trust, knowledge and protection of all users. Kushki in Latin America. In spite of the circumstances that affect us all at this moment, for Kushki it has been a lucky trip that has allowed us to be part of this evolution in the whole region. We have witnessed stories that engage us completely when participating in aid campaigns for the trade sectors (Peru), incorporating electronic payment methods for governmental entities (Colombia) and municipal governments (Ecuador). This situation has demanded us to develop new methods for money collection (such as our payment links) that facilitate the incorporation of online payments without the need for an integration. On the other hand, our service indicators have multiplied and our integration area has had to increase their efforts to deal with the scale of businesses that now need, more than ever, our support to enable online payments in the region. At Kushki, we are working to generate better solutions and payment methods, as well as agile support processes. Especially, we are listening to our customers; for them, we are striving to become the payment gateway that connects all Latin America.
Avatar img
Nahúm Ojeda
CPO de Kushki
junio 01, 2020

La importancia de ser ágiles

Quizás alguna vez has escuchado que crisis en japonés significa oportunidad, lo cual, en parte, es cierto. Realmente la traducción de crisis a nuestro idioma es: riesgo y oportunidad. Así, lo que nos pretende decir esta cultura milenaria es que, la crisis es el momento para atreverse, sacar provecho y evolucionar frente a los retos que nos impone el presente. Para aprovechar las oportunidades hay que ser ágiles. Esto implica estar atentos a los cambios y listos para afrontar situaciones adversas sin que estas lleguen a impactarnos más de lo previsto. Lo que a su vez, facilita la toma de decisiones con la confianza de tener el respaldo de un equipo comprometido. Es indudable el temor que genera para algunas organizaciones implementar la cultura del agilismo, pero este se puede reducir al contar con los aliados estratégicos indicados, que aporten valor y complementen con la experiencia que se requiere. “La transformación digital no es una opción, es una obligación” Hemos visto día tras día cómo las empresas están evolucionando porque el mercado así lo exige, y quedarse por fuera de esta transformación llevará al camino de la extinción. El comercio digital se encuentra en auge y se fortalece más cada día que transcurre. Los hábitos de compra de las personas están migrando al consumo a través de internet, donde contar con una pasarela de pagos segura puede hacer la diferencia para un negocio. Por esto, Kushki, garantiza que todas las transacciones cumplan con el estándar máximo de seguridad, al contar con la certificación PCI-DSS Nivel 1 propia e infraestructura robusta. Brindado así, tranquilidad a los compradores, al asegurar que sus pagos no estarán expuestos a fraudes, lo cual, lastimosamente es uno de los mayores riesgos de este mercado. En Kushki somos una familia 100% ágil que conoce las exigencias del ecosistema digital donde día a día surgen nuevas necesidades que nos obligan a estar listos para llevar soluciones factibles en tiempo y costo. En esta era, todo negocio que desee tener éxito debe mantenerse ágil. Pues, la agilidad crea una cultura empresarial transparente, donde todo el equipo se involucra generando una rápida capacidad de adaptación. Por otro lado, para generar resultados de calidad, las organizaciones requieren enfocarse en la mejora continua, a través de esfuerzos progresivos, tal como lo define el término japonés Kaizen, a fin de cambiar el status quo. Esto se logra mediante un mejoramiento continuo a través de pequeñas y grandes acciones de forma diaria. Es un concepto individual y grupal que lleva a cambiar la mentalidad de los miembros del equipo. Por lo que, constantemente inspeccionamos nuestros procesos para identificar oportunidades de mejora. Para asegurar agilidad en los procesos, existe una gran variedad de metodologías, entre ellas: Scrum, Crystal, XP, Kanban, etc. Cada una de ellas tiene sus propias características y puntos en común, pero lo más importante es que se pueden adaptar según las necesidades de la empresa o proyecto. Todas promueven valores como comunicación, compromiso, seguimiento y empatía en los miembros del equipo. Después de todo, un equipo unido y motivado genera grandes resultados. Todas las estrategias que puedan tomar las organizaciones encaminadas a mejorar la productividad son importantes, pero sin duda el agilismo es una opción que aporta grandes beneficios. Este requiere compromiso, ya que no es algo que se implemente de la noche a la mañana, sino una cultura que se construye día a día.
Avatar img
Rafael López
Business Analyst Lead Payments @ Kushki
mayo 25, 2020

In the land of coronavirus, technology rules

Adapting to technology: a new challenge for businesses Today, citizens' health and protection have prevailed over all other variables, stopping industries and processes. Their digital transformation has steadily become (and will be, even more) a strategic ally to increase the awareness that the value chain of enterprises (whether digitally or non-digitally native) does not need to be limited to the physical world for their transactional development. After we have got over this situation, consumers will change their habits and behaviors, and companies will have to adapt quickly. Public services are not and will never be external to these changes in consumers' behaviour. We have Colombia as an example, where the SNR (Superintendency of Notary and Registry, in Spanish) understood the need for citizen-focused adaptability (as well as for ensuring operational efficiency and transactional transparency), and increased formal integration with online payment companies to leverage the opening of high-traffic channels for the issuing of certificates and the payment of taxes by citizens. In a digital, efficient way, and without generating operational costs for development, they made feasible their digital transformation, adapting to their users, opening themselves to finding the correct allies, those who understood the value proposition of their business and their consumers. This effort by a public institution such as the SNR allowed for the massive increase of IT outsourcing by an institution that seeks to serve the greatest number of citizens, and also allowed them to maintain their growth ratio by reducing user traffic at their physical offices by more than 35% (through an exclusive platform, Kushki became the main certificate seller for the SNR in Colombia, and sold more than 180,000 certificates every month for the benefit of citizens) and reducing by 7% the issuing of physical certificates. This is an indicator of the citizens' open-mindedness about using new channels with a value proposition that adapts to his/her needs. This also requires an open mind from public institutions, but also a constant work, and support from the strategic partners who always have in mind who is facing the citizen. Transaction security, coordination, and adaptability for understanding the operational flow of public institutions, as well as constant feedback to manage UX flows in these processes, are part of the transformation process in efforts such as this one. The circumstances we're living in today will, without a doubt, set a precedent in the consumption habits and business models of some institutions. Today, they have had to speed up their digital transformation processes and modify their product development strategies, all because of a scenario that changed many business strategic plans that had been established in 2019. Never before, digital transformation has had a more aggressive sponsor, and this can be seen in the reaction that each of us has shown to advance our businesses, understanding that adaptability in times of crisis involves a development work and a constant learning, which will prepare us for post-pandemic businesses.
Avatar img
Oscar Salas
Country Head @ Kushki Perú
abril 24, 2020