How to Build Microfrontends with Angular

Moving from a Monolith to Microfrontends

Ashan Fernando
Enlear Academy
Published in
6 min readMar 31, 2020

--

Breaking an Angular App to Microfrontends

Over the past decade, JavaScript made its way through as a mainstream technology for web apps, and frameworks like Angular, React, Vue emerged. Today we can see many complex web apps built using these frameworks. As we all know, with the increase of complexity, things likely start to slow down.

We have experienced a similar situation while working on a complex Angular app with nearly 200k lines of Typescript code. The bundle size of the gzipped and minified JavaScript has gone well beyond 1MB (even with lazy loading), impacting the initial loading time of the web application. Besides, developer productivity also went down since it takes some time to build the Angular app. A large developer team was already working on the codebase, making it challenging to coordinate the development and testing of the functionality.

To address these challenges, we have decided to break the Angular app into Microfrontends.

1. Preparation Work

Before breaking the app to Microfrontends, initial work carried out to identify the boundaries and managing the dependencies.

Identifying the Boundaries

While finalizing the boundaries, we have considered the business domain segments, internal dependencies in terms of user journeys and code, team structure, and known future requirements.

Managing the Dependencies (Shared Code)

Managing the dependencies weren’t straight forward in the beginning. Initially, we thought of moving all the common dependencies across Microfrontends to libraries. But later, the decision was ruled out due to its complexity, which requires a significant overhaul of the app.

Then we decided to go ahead with Git Subtrees, where we keep the latest version of the shared code locally in each Microfrontend as well as in a central Git repository to copy across each Microfrontend.

If you plan to compare different approaches in managing shared code across Microservices or Microfrontends refer my article on The Dilemma of Code Reuse in Microservices.

Restructuring the Angular Application

After identifying the boundaries of the Microfrontends, we started to restructure the Angular modules and components. The main objectives of restructuring were as follows.

After Restructuring the Angular Application
  • Reorganize the Angular modules and components folder structure so that we can later extract them into each Microfrontend.
  • Identify the Angular modules and components, which have dependencies across Microfrontends.
  • Define the ownership of these modules and components and grouped them inside the shared folder, such that the ownership is visible.
  • Reorganize global dependencies, both app-specific and external (Utilities, Auth OIDC, NPM Modules &, etc.).
  • Identify shared states across Microfrontends.
  • Prefix Angular routes with the relevant Microfrontend path.

While doing the above, we need to test the app so that the functionalities remain intact without breaking the app.

2. Breaking into Microfrontends

After restructuring the Angular app, we have created individual repositories for each Microfrontend and an additional one for shared code.

Then we copied the Angular app code into each Microfronend repository and deleted the code that doesn’t belong there. For example, after copying the Angular app code to Microfrontend 1 repository, the Microfrontend 2 and Microfrontend 3 code are deleted.

After that, we have copied the shared code to a separate Git repository. Each Microfrontend repository has a copy of the shared code locally. Besides, the shared code also resides in a separate repository, which keeps the code synced with its local copy inside each Microfrontend, using Git Subtree.

Then we have to modify the main navigation menu in each Microfrontend to link with each other by URL. These links in the main navigation menu are direct URLs that point to each Microfrontend Angular deployment.

After breaking into Microfrontends and Relevant Git Repositories

Note: When deploying the Microfrontends, you need to configure either a Gateway or a Proxy in front so that we can route requests to relevant Microfrontend using path-based routing.

3. Improve User Experience when Switching Between Microfrontends

One of the main problems that appear when navigating across the Microfrontend is the loading time of each Microfrontend. Before breaking into Microfrontends, a user can click on the navigation where the Angular app activates a route. This navigation happens fast since its only a router activation. However, when the navigation happens across Microfrontends, it is a full-page load which is typically visible to the user. Though, the index.html loads instantly, each Angular app in Microfrontend takes time to initialize since it requires to load the JavaScript bundle. This delay is enough to show a flickering effect when navigating across Microfrontends.

As a solution, we have embedded the main navigation menu and the header into the index.html for each Microfrontend. The JavaScript that needs to run for the navigation and header elements were inserted directly into a script tag inside the index.html.

This approach eliminated the flickering effect where the main navigation menu and the header appeared unchanged to the human eye when navigating between Microfrontends.

4. Sharing State Across Microfrontends

When breaking an Angular app into Microfrontends, a common problem occurs when sharing state between each Microfrontend. If there aren’t any complex states in JavaScript memory in the existing app, we could use URL parameters to share state across Microfrontends. Fortunately, we didn’t have any complex states. But as a precaution, we have decided on an approach to share complex states.

The solution is simple, where we serialize the complex state into a Base64 string and pass it as a parameter from one Microfrontend to the other. When initializing each Microfrontend, it will check for this unique state parameter and initializes its state accordingly. However, there is a size limitation when serializing the state into a string. If the serialized state size becomes a limitation, you can consider using Browser Storage (LocalStorage or SessionStorage) to share state across the Microfrontend. Besides, use this approach as a temporary solution to leapfrog the migration process. Later you have to refactor your application to eliminate complicated state sharing.

5. Summary

Overall, the approach discussed in this article is proven to be effective in breaking down a large Angular app into Microfrontends within a short time. This approach is equally valid single-page applications build with React, VueJS, or any other frontend frameworks. However, there are numerous challenges while following this approach. In this article, I have only highlighted the fundamental problems and solutions which we have experienced while breaking an Angular app into Microfrontends.

One of the key lessons we learned is to focus on keeping the application functioning and, if broken, bring it back to functioning state using different tactics. You don’t need to solve all the problems entirely but keep an eye on the overall impact when connecting all the Microfrontends to make the application functioning.

Another key learning is that it is more efficient for a small team to do a proof of concept where they could discover most of the bottlenecks before the entire team jumping into action.

Also, there are several pitfalls in this approach. For instance, the codebase in the Shared repository is challenging to be tested in isolation, since it has a sense only when combined with the Microfrontend code. Unless you refactor the shared code to function independently, this is a significant challenge for testing.

Besides, I won’t recommend this approach to a small or midsize Angular app since the overheads might surpass the benefits you gain by breaking into Microfrontends.

But should we start a new Angular app as Microfrontends? In my opinion, the answer is still No since it is easier to build it as a single Angular app at first, where it is difficult to rationalize the additional efforts in managing Microfrontends at the beginning.

At last, breaking an Angular app into Microfrontends is just part of the story. More complications come when breaking the Backend to Microservices, which needs to work inline with Microfrontends. Since this is a broader topic, I will keep it for another article.

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

--

--

Published in Enlear Academy

We provide high quality content on web development and cloud technologies for developers.

Written by Ashan Fernando

Solutions Architect and a Content Specialist. For more details find me in Linkedin https://www.linkedin.com/in/ashanfer/

Responses (6)

What are your thoughts?