Kiril Zafirov

Front End Engineer at IT Labs

This article describes how to achieve frontend architectures using multiple frameworks and libraries with a particularly important goal in mind. i.e., combine into a single web application while displaying one result (as illustrated in Figure 1), and at the same time not affecting the user in terms of how he sees the page.

The technique of micro frontends is the process of decomposing a large frontend monolith application into smaller chunks that can be developed, tested, and deployed independently while still appearing to the user as a single product.

Figure 1: Multiple micro frontends combined into single page

What are micro frontends

"An architectural style where independently deliverable frontend applications are composed into a greater whole"

– Martin Fowler –

The idea behind micro-frontends is to present the business subdomain and allow the team-independent implementation with the same or different technology choices, as illustrated in Figure 1. The web application is a composition of features owned by different teams, each with different domain problems. The web development’s front end can (and should) follow the same structure. This is where the micro frontends come into play. Each of the micro frontends can be independently developed and tested in terms of features and responsibility.

Micro frontend architectures’ requirements are a shared codebase, preferably in JavaScript, managing routing and user-sessions. Additionally, maybe a thin collection of style sheets, a collection of separate modules “mini-apps,” various built-in frameworks and possibly stored in different repositories, and a deployment system that bundles all those modules together and deployed to a server.

We will touch on the following in this article:

  • What are the pros and cons of this approach
  • The different approaches/implementations that can be taken to build such architecture
  • Cross-Application Communication
  • Routing

Reasons why you should move to micro frontends

As software architecture evolves, the front end should follow that evolution as well, as shown in Figure 2. If we look back, the applications were built as large monoliths. This slowly progressed to microservices, but the UI got stuck as one large monolithic codebase, structured around the solution as a whole and not around a certain domain. This way, even the smallest of changes that needed to be deployed would’ve meant redeploying and running the pipelines for the whole front end again.

The proposed approach of micro-frontends came about naturally by following the microservices pattern,

Figure 2. Software Architecture from Monolith to Microservices

where it would be dividing the teams as independent owners of the whole feature end-to-end.

Each team would be responsible for the creation of the UI portion of the application, styling, code, layout, architecture, backend, devops, and connection to the database.  It has its own build and test pipelines and CI/CD procedures. This enables the following:

  • Better scaling capabilities of the frontend application
  • Fewer communication issues between the multiple teams for one feature change
  • Reduced code and testing complexity
  • Aids with the application being more resilient to non-working or broken portions
  • Helps to overcome the law of diminishing returns
Figure 3. End to end teams with micro frontends

Several companies and projects are running into the issue of having old, large codebases demanding full rewrites. These were probably the side effect of pressure to get faster to production and market, instead of evolving the product for maintainability. This is creating a situation that can be seen as a time-consuming rabbit hole where developers get weighed down by old tech stacks or the large monolith’s sheer complexity and subsequently unable to deliver new features to customers without being held back.

And usually this is where incremental updates start with having one team getting the first feature as a micro frontend delivered to production and the rest of teams will follow. The end-game is that we are free to make case-by-case decisions on individual parts of the product and be able to make the incremental upgrade to the existing architecture. Each micro frontend can be upgraded when needed or makes the most sense and be redeployed as a separate portion, rather than having to upgrade and migrate everything at once. If we want to experiment or do the new feature in the latest technology that is still in the experimental version, we can do that in a more isolated fashion than we could before.

The source code being delivered for each micro frontend is much smaller than the source code for the whole monolith which is also a plus because you can implement lazy loading and improve the performance of each service separately and thus on overall on the application and the user experience. The code is isolated and may be placed even in different repositories. There is a clear line around the bounded context and what should the application contain according to that context. Micro frontends architecture forces your decisions to be explicit and deliberate about data and events flow between portions of the application.

Independent deploy ability of micro frontends is key to success! This reduces the scope of any given deployment and all the risks that comes with it. Autonomous teams have full ownership of everything and they deliver the value to users which enables them to move quickly and effectively and as described in Figure 3. Teams need to be formed vertically for each functionality on the contrary to technical capabilities.

Monoliths are not bad but at times they are hard to manage. In a large monolith with pieces tightly coupled and interdependent, whatever change, upgrade, update, fix, no matter how small it is it demands the whole monolith to undertake the change and be released, and in cases if there is sub-dependents on the monolith they will also be affected.

Benefits

Some of the key benefits of micro frontends include:

  • Small – Smaller, cohesive and better maintainable codebases
  • Autonomy – Autonomous teams can develop, test and deploy independent features from start to end on themselves.
  • Manageability -The ability to rewrite, update or upgrade parts of the frontend in a more incremental way than it was previously possible
  • Ownershipability for a single team to decide and take responsibility for a feature

How to micro frontends

In order to be successful with micro frontends there a number of cases that need to be discussed and decided. From an implementation point of view, there are certain principles that need to be followed

  • Define – Each micro frontend needs to be defined around a business domain or a feature. Model around the business domain
  • Compose – How each micro frontend is going to be composed
  • Route – How is it going to be called or routed to
  • Communicate – How is it going to communicate with the rest of the application
  • Decentralization – Separate each micro frontend in its own repository , create independent pipeline as you are working in a single independent application
  • Orchestration – bringing all the micro parts to work together
  • Independent deployment – The team owning a micro frontend should be able to independently work, update, deploy, redeploy the application without touching on any of the other micro frontends.
  • Isolation of failure – When one micro frontend is not working properly or down that should not affect the other existing applications.

Before starting developing and architecting a solution we should also have in mind that the solution that we are going to build should be technology agnostic.

  • All of the code that is going to be written should be isolated and independent per micro frontend
  • Each team should posses its own prefixes and they should not come into conflicts with other teams
  • Stick to the native browser features and do not go and invent your own
  • The application that is going to be built should be as resilient as possible (Universal Rendering and Progressive Enhancement to improve perceived performance).

There are implementations ways of achieving micro frontends and we will be discussing some of them next.

Figure 4 Micro Frontend Techniques

There are two approaches to micro frontends based on the type of runtime they have and both with its sub-approaches. As the name suggests, these are being divided based on the environment the micro frontend has and if its being shared between multiple micro frontends a.k.a shared runtime or does it has its own separate runtime.  This two further branch out as it is being displayed in Figure 4.

Shared Runtime

In this approach the runtime of the application is being shared between the micro frontends. Thus we might conclude that if there are two or more micro frontends in the same page and one of them is doing some heavy computation or requires the main thread more, this might cause blockage for the other micro frontends that are on the same page.

Web Component

Pros of this approach are :

  • Web components are more or less encapsulated depending on if Shadow DOM is used or not.
  • Leverage common assets and consistent look and feel.
  • Easy to consume shared code and reduced overall amount of code that needs to be shipped (also prevents code duplication).
  • SEO and A11y are being treated as any other HTML code.
  • Fully framework agnostic and can be used as both web app or mobile hybrid application.

Cons to this approach are:

  • Bad support of older browsers (using pollyfills).
  • Initializing and bootstrapping multiple components in a page can cause performance impact.
  • If the application is bundle as a whole, any small change can trigger the whole build/deploy cycle for the whole application.

Framework Based Components

These are usual framework build controls and can be built with any of the existing or the framework to come. The challenges that need to be overcome in order for this approach to work are the vendor lock-in, they are difficult to migrate and it is extremely difficult to combine with multiple frameworks. They are not tech agnostic and they are hard to be build as autonomous features.

Transclusion

This includes applications that are being rendered at the server side using various techniques to do server side rendering and the combination of various applications in order to be delivered as a single page. The benefits of this approach are simplified client side processing and user experience, but the cons to this approach is that there is no native way of doing it and that it slows the overall page.

Separate Runtime

In this approach each micro frontend has its own runtime and it is fully independent of the other micro frontends. All of the micro frontends should be able to run in parallel without any taking the main thread and blocking the others.

MicroApps

When using the microapps approach tend to build features horizontally by vertically dividing teams, so in terms of meaning when you start developing the micro frontend respect the tradional development with embracing the javascript ecosystem and dynamic rendering, whilst investing up front in setting up teams structure and overcoming testing and scalability challenges and also dependency management.

With micro apps the user experience is slightly off because each application is isolated and has its own styles, framework, team and code logic, so there might be places where UX might be off. Other then that micro apps reap all the benefits from this approach meaning they are autonomous, tech agnostic, value and microservice driven, and they are being owned by a team. The combination of the application can be done on both the frontend side and on the server side and this will change complexity of the application transfering the complexity to the side that is going to be doing the combination. If done on the client side browser would include the content using AJAX and this reduces the backend complexity but it will introduce huge front end complexity and performance problems.

This approach is also not tech agnostic and tends to deliver bad user experience:

IFrames

  • Pros of this approach are:
    • That the team developing the application can be fully independent also in terms of choosing their own technologies and cycles

    The application is fully encapsulated in the iframe, so it does not affect The Host javascript or css.

    Cons to this approach are:

    • A lot of duplicated code if more then one micro app is initialized in a single page and this is mostly because assets, fonts, styles cannot be shared between host and app
    • Routing can become very complex
    • Difficult internationalization, preservation of unified styles
    • Additional code to handle iframe resize and problems that come with iframes
    • Not so much SEO and A11y friendly

    No matter what of the upper approach you would be taking, Orchestration for bringing everything together is needed in order to combine all the micro-parts to be functioning and working together. This can be assured with having a dedicated team to do just that and that team is called ‘The Host’. This team is in charge and responsible for orchestration and composition and is responsible for

    • Configuration for initialization
    • Routing
    • Communication between components
    • State Management
    • Providing commons/shared code
    • User triggered event-based API’s

    In order to dive deeper into micro frontends there are a number of useful API’s that can excel the composition of micro frontends.

Common considerations and downsides for all micro frontend implementations

Some micro frontend implementations can lead to duplication of dependencies, increasing the overall size bundle of the application and the bytes the user must download. As a caution that you should be also aware is because of the team autonomy each team can work in a separate fashion and that can cause team fragmentation.

Operational and governance complexity

As number of micro frontends grows so does proportionally the number of repositories, tools, build/deploy pipelines, servers , domains etc. So, before continuing further, code quality and consistency should be agreed upon, the governance of the teams should be assigned, automation for all the processes should be provisioned, and the processes set should be able to scale.

When you choose micro frontends, by definition you are opting to create many small things rather than one large thing. You should consider whether you have the technical and organizational maturity required to adopt such an approach without creating chaos.

 Cross-Application communication

One of the most common questions regarding micro frontends is how do we make it available for them to communicate with each other. And the saying goes that we should keep it to a minimum and if they still need to communicate, group them and bundle them together because with high communication frequency they should be part of a single piece. With each communication connection we add between them we introduce a dependency between them which we initially were seeking to avoid. Anyway this wouldn’t be a valid communication section if we didn’t mention a number of communication ways so:

  • Custom Events – They allow micro frontends to communicate indirectly which has a good side that minimizes direct contact and coupling, but also on the bad side it is harder to determine where the event originated from and enforce a contact that now exists between the micro frontends
  • Event Bus – There is a custom event bus strategy that allows for the creation of the event bus on “The Host” micro frontend and importing it the micro frontends that need to communicate with each other
  • Backend – Communicate through push and pull based communication via an API connection through the backend
  • Routing – Address bar as a communication Mechanism

No matter the approach that we choose we should strive to have our micro frontends communicate by sending messages or events to each other, and avoid having any shared state.

Routing

It is best to have a separate page for each micro frontend and not to mix them too much on a single page. So the recommended way of handling the routing is to give the control to “The Host” micro front end and he is responsible for doing the navigation and loading of the bundles.

https://host.com/Team-A

https://host.com/Team-B

https://host.com/Team-C

Testing

Testing each micro frontend should be easy and simple as testing a monolith with the exception that not a lot of external dependencies and connections are required.

Existing Framework supporting micro frontends

The framework that we used in our example — as of this writing, one of the best out there —  was Single Spa (Micro frontends approach that is composing multiple SPA’s together built by Canopy) with the support of System.js where you can define a single root micro frontend that is going to provide the previously talked “The Host” capabilities to all the rest of the applications. Some of the rest are :

  • Bit – which is component-based approach. Different teams can build, publish and expose their components independently, while you can also integrate and compose independent components together.
  • FrintJs – It lets you load apps coming in from separate bundlers, gives your applications a structure, and handles routing, dependencies and more. (mostly for react)
  • Mosaic – Mosaic is a set of services, libraries together with a specification that defines how its components interact with each other, to support a micro-service style architecture for large scale websites.
  • Podium – server side composition of micro frontends
  • Polymer – Web components approach. This is a Google-owned project.
  • OpenComponents – is an open-source, “batteries included” micro frontends framework.

Micro frontends – Demo project sample

We will be using the Single Spa approach to building a demo micro frontends application.

The application that we are going to build is displayed on the following image:

Figure 5: Micro frontend demo application

There are two types of highlights in the figure above and the red highlight is for the Navigation bar application which will be active all the time together with the root application – “The Host” and the rest of the applications are highlighted in green and they are as the name suggests Account Settings micro application built with Angular v10, Admin micro application also built with Angular v10 and Users micro application build with React v16.

Here you can have at least two more micro applications based on the architectural decisions that you need to make and which approach should you choose. You can have application with shared libraries and resources and also a micro app that will import the general styles for all the micro frontends. In our approach we choose not to use any shared styles, libraries or modules in order to not have any coupling between the micro applications.

Create and configure single-spa app

In order to start with developing the micro frontends applications you need to have single spa installed globally (or locally on your machine). In order to install it, type the following command in your command prompt:

npm install —global create-single-spa

Assuming that you are already in the folder that you want to start working with micro frontends you can go ahead and type in:

// Creating the single-spa app       
npx create-single-spa

if not, you can use the following commands to create a directory and navigate to it from the command prompt:

// Creating the folder for all projects
mkdir microfrontends
cd microfrontends

// Creating the root app folder
mkdir root-config
cd root-config

and then type the command for creating the single spa root application:

// Creating the single-spa app       
npx create-single-spa

So, after running this command you will be prompted to fill out a number of additional inputs via the command line and you should select this according to your need (you can see on the Figure 6 bellow for information on how the prompt looks like).

For Directory you can type free text to create a directory for where you want the cli to generate the new application,  for new project you can select one out of three options that are there

  1. single-spa root-config (you can have only one of this kind)
  2. single-spa application / parcel
  3. in – browser utility module( styleguide , api ,cache , etc) as shown in the figure below:
Figure 6: single-spa app configuration

Some of the other questions that you are asked are which package manager you would like to use and you can choose npm or yarn, will this project use typescript and you can choose Y/N, if you would maybe like to use the Single-spa layout engine which is very useful it asks for an organization name and a project name and you are done.

Figure 7: single-spa app additional configuration

The important one is the second one which asks you to Select type to generate? and for that you should enter single-spa root config in order to generate the root micro application.

After generation of the root config, go ahead and for each application create a new folder to put the micro application in and repeat the step of:

npx create-single-spa

you should now select:

single-spa application/parcel

and for framework you can choose which one you wish to use.

Repeat this step for all the applications that you need (Account Settings, Admin, users)

When you finish with the installation of each application in the console logs you see the following output:

Figure 8: single-spa creation output

You can choose to start it locally or follow the steps 2, 3 and 4 in order to test it in the browser in the single spa playground.

We used System.js to create import maps for all the micro frontend applications that we needed and imported them into the root config. For production and for development use these can be set to be loaded dynamically.

System.js allows us to import a module over the network and map it to a variable name.

Figure 9: System.js configuration

Each micro frontend application should have different ports.

App configuration

In order to register the application you need to define it in the root-config.js .

Figure 10: App configuration – root-config.js

All of your micro frontend’s applications must be registered in this file so for each application we would have a similar registerApplication function call like the one shown below. The name usually follows the pattern of “@organization/appName”.

We used eev for communication between the micro frontend applications and you can add a number of custom properties based on the needs of the organization.

To define cases where you want them to be valid only for certain environment you can wrap them in an “if” statement following the pattern shown in the figure below (This is in the index.ejs file in the root-config application).

Figure 11: Environment configuration

For the layout of the application, we used single spa layout which is a helper library from single spa that allows us to write template in the head of the html document in order to specify the layout of the application:

Figure 12: single-spa layout

If you want to checkout the full source code of this example you can do that here.

Single-Spa wrap-up

Single-Spa supported with the optional helper package  single-spa-layout  is a great framework and you can do a lot with it. It provides micro apps layout, routing, lazy loading, top level routing API controls, apps out of the box.

It supports most of the existing popular frameworks and libraries out there (at the time of writing this article) so that you can go ahead and create a new application add single spa to it and transform your application in a micro application. Because it has a lot of features, the best way to learn more about the framework is to go through the official documentation.

You can access the full source code of this example here. Have a great micro-frontend-programming!

For more information on how to proceed, go here.

Conclusion

Be mindful and use architecture patterns when needed. There are valid use cases for micro frontends, where they are probably the best solution, but also there are use cases where micro frontends are just a burden and too much of a complexity without any real benefit.

“Architecture is aways a trade-off – just find the right approach based on your context”

Luca Mezzalira –

To make micro frontends possible you need at a bare minimum:

  • Solid event driven strategy
  • A team in charge of orchestration, routing and commons
  • Well documented and defined style-guide
  • Performance budget and mechanism in place to accelerate load

CONTACT US





    By submitting your information, you are automatically accepting the Privacy Policy and Terms and Conditions of IT Labs. The information submitted to IT Labs will not be used by our partners and will not be shared to other Companies to be used in Marketing purposes.