Until recently, JavaScript was used for such primitive tasks as changing the color of text on a page. The web began to grow rapidly, and as a result, the complexity of web applications has increased. Over the past 10 years, most of the programs that we use every day have migrated to the web. Now it’s hard to imagine our life without Google Drive, Google Docs, YouTube, etc.

In this article, we will talk about the microservice approach in web development of user interfaces.

A typical web application consists of HTML-layout, CSS-styles and JavaScript-code, which allows to achieve the maximum level of interactivity and responsiveness. The higher  complexity of the application, the more complicated the user interface, and as a result, the tools that are needed for its development. That is why front-end development has evolved from a simple set of add-ons for the user interface to a complex ecosystem with many tools and a high entry threshold.

Problem and Solution

Web user interfaces continue to evolve.

According to statistics, every month the volume of JavaScript code in web applications increases, which on most projects leads to an increase in the following parameters:

  •     development time due to the high level of code complexity
  •     testing time
  •     time interval between releases.

Large applications that have such problems are often called monoliths because of the architectural approach used.

Monolithic architecture is an architectural approach in which all the core logic of an application is gathered in one place. A monolithic application consists of a single layer combining different components into a single whole.

Development of new functionality in such an application, where a large amount of outdated code, is becoming more and more expensive for business every year. Backend developers have already faced a similar problem. One of the solutions to most of the problems of monolithic architecture is called Microservices.

Microservice architecture is the exact opposite of monolithic architecture. Using this approach instead of one large application, we create a set of small loosely coupled and easily replaceable modules that interact with each other. One of the main advantages of microservice architecture is the ability to use the best technical stack for every single task.

In addition, we can distinguish other advantages of microservice architecture in comparison with monolithic:

  •     modularity
  •     reduction of time for testing
  •     reduction of time for deployment and the ability to do this in parallel
  •     the ability to scale the team horizontally.

Is it possible to use the microservice approach in web development of user interfaces? The answer is yes! It is possible and necessary! It is this approach that is called micro frontends.

Micro frontends

Micro frontend is an architectural approach in which independent applications are assembled into one large application. It makes it possible to combine in one application different widgets or pages written by different teams using different frameworks.

The main advantages of the microfrontend approach are in the development of large enterprise applications:

  •     modular architecture. Separate widgets or pages are completely independent applications
  •     testing speed. Changes in one widget or page can be tested in isolation and only in this application, without wasting time testing the rest of the functionality
  •     parallel deployments. Individual widgets or pages can and should be deployed independently.

In addition to the obvious advantages of this approach, it has significant disadvantages:

  •     increase the overall complexity of the application
  •     duplication of code. Each application is developed by a separate team that makes its own technical decisions. This leads to reloading of the same frameworks, libraries and general duplication of code that could be reused
  •     A monolithic application JS bundle will always be smaller than a collection of bundles in microfrontend architecture
  •     possible problems with caching and versioning of applications
  •     global variables or CSS styles are things that you should forget about in the microfrontend architecture if applications are not completely isolated.

Using this architectural approach on small projects and in small teams brings more problems and additional development complexity than advantages. But large projects, along with distributed teams, on the contrary, receive greater benefits from the creation of microfrontend applications. That is why today the microfrontend architecture is already widely used by many large companies in their web applications:

Unfortunately, while there is no specific specification for building a microfrontend architecture. Here are perhaps the most accessible and simple ways and techniques for building microfrontend applications:

  •      IFrames
  •      Tailor.js library
  •      single-spa framework.

Let’s go through each of these approaches.

Iframes

IFrame is a long-standing technology, which, despite its irrelevance, makes it possible to build a microfront architecture. Using an IFrame, each individual widget can be placed in an IFrame that loads the desired application. Using this approach, you are likely to encounter the following problems:

  •      performance
  •      complexity of support.

I highly recommend not building a microfrontend architecture using an IFrame. Today, there are other ways to make it easier and more efficient.

Tailor.js library

Zalando has created an entire ecosystem for building microfront architecture, and Tailor.js is part of the ecosystem. The peculiarity of Tailor.js is that it is a package for Node.js, and it is focused on building a microfrontend architecture with server rendering.

For me, an additional disadvantage of this package is the lack of documentation. I could find live examples of projects only in separate GitHub topics, where ordinary users ask for advice and attach links to their repositories with code.

Single-spa

The main and, in my opinion, the best approach in building a microfrontend architecture is the single-spa framework. Here are the main reasons I recommend choosing single-spa:

  •     working examples using modern frameworks and libraries
  •     good documentation
  •     the ability to share dependencies between individual widgets
  •     support for independent deployments
  •     building the application on the client side
  •     ready-made ecosystem of wrappers for quickly integrating existing applications into the microfrontend architecture.

Single-spa is a framework that makes it possible to combine different applications, regardless of the library or framework used, into one. Under the hood of a single-spa is a set of existing tools along with our own solutions:

  •     SystemJS – module loader, which is needed for asynchronous loading of individual applications
  •     wrappers – single-spa provides separate wrappers for each framework that creates a wrapper over an application, which is necessary for integration and connection of a separate application into a common single-spa
  •     API – single-spa provides a set of tools that are needed for communication between individual applications, subscribing to events, etc.

A typical application using single-spa looks like this:

And here is the communication between the individual elements of the microfrontend architecture built using single-spa:

In the Root Application a sigle-spa is connected as the main framework, as well as SystemJS configuration for the correct loading of external applications.

For each integration application, each child application must provide public methods bootstrap, mount, unmount, which are used by the single-spa framework for manual bootstrapping of the application. Almost every modern framework has a ready-made wrapper that simplifies this task and automates some part of the process. On the single-spa website you can find a list of all the frameworks for which ready-made wrappers exist.

It is possible to build a set of applications using the microfrontend approach and single-spa both by creating the entire infrastructure and applications from scratch, and based on the existing application. Let’s look at examples of how it looks, creating a set of completely new applications using React.js and Angular 8.

Using the existing single-spa wrapper for React.js, we create an interface with the bootstrap, mount, unmount methods, where we respectively describe how our application should bootstrap. Wrapper helps to encapsulate internal implementation and create an API for proper connection to a single-spa framework.

It looks the same for Angular 8.

As with React.js, we provide an interface for manually bootstrapping an application on Angular 8.

In addition to using specific wrappers, the application should be built as an amd module. Each such module is asynchronously connected to the root of the entire microfrontend architecture – Root Application. Below is an example of elementary implementation.

Two main parts that you should pay attention to:

  •     a script tag in which it is necessary to describe the mapping of the application name to the address from which single-spa will load the amd module for this application
  •     script tag where you need to register the application directly using the registerApplication method. Here you need to specify the address, upon transition to which system.js will load the corresponding module.

As you can see, Root Application is a simple HTML file with basic configurations for loading other applications. You can register many micro-applications in one microfrontend application. If, when registering the application, simply specify / in the 3rd parameter of the registerApplication method, such an application will be loaded for each available address. It is this approach that is desirable to use to create a navigation bar or parts of the application that are common and should not be reloaded when switching between applications.

We, developers, do not always have to create applications from scratch. Single-spa makes it possible to fully use existing applications as elements of a microfrontend architecture, whether it be Root Application or asynchronously loaded micro-applications.

If you need to create a micro-application that is compatible with single-spa and has the ability to boot asynchronously from an existing application, first of all you need to look for the appropriate wrapper. For most modern frameworks and libraries, single-spa provides wrappers ready to be downloaded via npm, as well as documentation describing how to configure them correctly. If, for the framework used, there is no ready-made wrapper, then you can always write it yourself. Having configured the wrapper and having assembled the amd module, you will get a microapplication ready for connection; all that remains is to add the appropriate configuration to the Root Application. From my own experience I will say that for modern frameworks and libraries, such as Angular 2+, React, Vue, which are assembled using Webpack, conversion to a micro application is quick and without additional work.

If you need to create a Root Application from an existing application, then first of all you need to connect the libraries for single-spa in index.html and add all the configurations necessary for downloading other micro-applications. After your index.html has become Root Application in single-spa, you can begin to configure the rest of the application to work correctly in single-spa architecture as described in the previous paragraph.

Problems and weaknesses

A big problem for both tasks is legacy applications written on old frameworks and built using old tools. For example, an application in AngularJS or Backbone that is built using gulp or grunt can “eat” a lot of your time before it is configured correctly. From my own experience I can identify the following problem areas:

  •     dependencies – a lot depends on how dependencies are loaded in your application. Anything that is not import can potentially cause problems at the build or run stage
  •     styles, pictures, fonts – most likely, all of this will be broken or with bugs after the first successful assembly of your micro-application.

At the configuration stage, the problems that you encounter will be the most diverse, so please be patient.

In addition to a complex configuration, we highlight the problems that may arise when building a microfrontend architecture using single-spa.

  • caching micro-applications. Without the right caching strategy, micro-applications, like the Root Application, will be cached in the browser and ignore any new changes that are relevant
  • debugging. If something doesn’t work, debugging without additional configurations and add-ons can be quite a difficult process, since you will not have to debug applications, but separate amd-modules without sourcing
  •   the total size of the application, along with all asynchronous micro-applications, will increase, in comparison with the monolithic approach
  •   the overall performance of the microfrontend application will be lower than that of the monolith
  •  code repetition. Re-loading the code – both libraries and entire frameworks – leads to poor performance of the entire application, and not just a single page. Single-spa makes it possible to share dependencies between applications, but remember: the more you have dependencies, libraries, components that are shared between applications, the more your microfront architecture looks like a monolithic one
  •    SEO optimization. Downloading individual applications along with bootstrapping takes place entirely on the client, which greatly complicates SEO optimization. Single-spa does not support server rendering, you can use the single-spa + Tailor.js bundle to add support.

Conclusions

Microfrontend architecture, like any other architecture, solves some problems by creating others. Microfrontend architecture will complicate your life and the life of your team if:

  •     less than 10 people are working on the project and there are no plans for expansion
  •     your application does not consist of abstract isolated modules, which in the future can be separate applications
  •     Bugfixing along with the development of new features is quick and without difficulties associated with the size of your application.

One of the common questions: is it possible to use single-spa to migrate from one framework to another (for example, from AngularJS to Angular 2+)? It is possible, but not necessary: ​​almost always there is an easier and more native migration method that requires less configuration work and which will work faster.