Micro Frontend — is a client-side architecture design when individual components or pages are hosted in separate domains and integrated into the main shell app (host application). Each of the micro-app owned the whole business subdomain and has the following characteristics:
Owned by one team. It doesn't mean that only a single team will be responsible for fixing all the bugs. It rather means each subdomain has a dedicated team that carries the domain knowledge about business logic and tech stack. It brings benefits to isolate a part of the business, and resigning of a single individual will not have any effect on product and business. You always reach out to the team, not an individual with any question about the business subdomain. Everyone in the team has to transfer personal pieces of knowledge to the rest through documentation and knowledge sharing sessions.
Independent implementation. The team that owns the micro app has the whole freedom in choosing a tech stack. It could be a different framework, coding practices, or architecture. Even though, the more freedom-the more flexibility, it is highly recommended to be aligned with other teams that own another micro app at least about the framework to choose. In this way, it will simplify the engineer’s transition between the teams and make it possible to share and integrate libraries with reusable widgets.
Independent deployment. Each of the micro-apps has to be deployed individually as they are hosted in a separate domain. It forces you to build multiple pipelines, which may be challenging sometimes. First, you need to pay attention to order, in case some of the micro-apps share common dependencies, libs, components, and utils. Also, make sure to make each of the pipelines as decoupled as possible. It will make it possible to run them in parallel and sufficiently increase a building speed.
Decoupling. Micro-apps have to be as decoupled as possible. The dependency between them ideally should not exist. They only can share the data, like state, UI widgets, Interfaces, and util helpers through the shared library. This part requires additional work during the architecture design phase, in order to build proper abstraction. On the other hand, it is important to find the balance, and not overengineer.
Fault tolerance. One of the biggest benefits of micro frontend architecture is reliability. If one of the micro-app is broken, the other will still be functioning. It is very important for huge web apps, where a single app has multiple separate big features. Let’s imagine Facebook, if one of the features, like groups, is shot down, users will still be able to chat with friends.
Robust Micro Frontend Architecture
What are the ways of implementing micro frontend architecture, there a two possible approaches:
Micro frontend as a component. In this case, micro-app is more granular. It acts as a widget that is reusable across applications. The problem with this approach is it makes the micro app couple more tightly. It may be complicated to build proper abstraction and can easily eliminate the advantage of micro frontend architecture.
Micro frontend as a page. With this approach, we dedicate a separate page per each of our micro-app. It allows us to avoid tight dependency between parent-child, compared to the previous approach. As a result, it is the most common way of implementing micro frontend architecture.
Ways to share the data between micro apps
One of the biggest challenges in architecting micro frontend is data sharing. The data can have different types like state, UI components, or utility functions. Make sure that your micro app is not containing a data store state, it has to be separated into a shareable data layer. You can imagine it similar to frontend — backend communication, where frontend is micro-app and backend (data layer) is the shareable library. Additionally, UI components and utilities will also fall into the same bucket as reusable shared libraries. This concept closely overlaps with monorepo principles. It guarantees consistency between the apps through the shared data.
There are also some other ways of communication between micro apps we need to mention. First, we can utilize our browser storage capabilities. It may be either localStorage, sessionStorage, cockies for storing session data or user metadata, or some more complex data structures in indexedDB. In addition to that, you may also establish the data transfer between the micro-apps simply through the router query params.
Angular provides already defined architecture that perfectly matches monorepo principles and micro frontend best practices.
The high level of architecture is divided into three major parts: workspace, projects, and libraries. By initializing an angular workspace — we generate a skeleton of the monorepo application, which further consists of projects and libraries. Each project could be a micro-app, it has an individual entry point and may have a flexible and customizable structure. Then libraries, on the other hand, are sharing the reusable data between projects, it may be our data layer, UI components, and other utility functions.
Angular modules are serving as the main building blocks. They encapsulate the entire feature, set of components, or a page with its dependencies. It is the perfect option to wrap our micro frontend app into it.
The routing feature comes with Angular out of the box and you don't have to install external packages. It provides everything you need to maintain the page transition between micro-apps. Routing outlets will allow you to configure shell app, hosting the navigation to each page of micro-app. You can choose which route will load which angular module.
And finally, lazy loading, which loads the angular module when needed, reducing the bundle size of the app and accelerating the page load. Angular has a native integration between routing and lazy loading modules. The only limitation is that it loads in compilation time, but what we need is to load modules dynamically, in runtime. We will show you how to do it shortly.
How to build?
Well, we have already reviewed the micro frontend best practices and figured out how angular is suitable for implementing such an architecture. There are a few grey zones left.
The first is how to load modules dynamically in real-time. This issue can be easily solved with Module Federation plugin, provided by webpack. It supports local and remote modules to build independently and asynchronously, which is optimal for our case.
The second question is how we combine all these sources together to create a workable prototype, which we can iterated over. The quickest way is to use NX cli, which will generate all the boilerplates for us. In order to learn more, check the step-by-step tutorial how to build micro frontend with angular, NgRx state sharing, using NX command line.
We found out that micro frontend architecture is tightly compatible with Angular. There are multiple tools to glue them together, like Module Federation and NX monorepos. We can only make a guess if the future version of Angular will support the micro frontend architecture from out of the box.
Source: Medium - Vitalii Shevchuk
The Tech Platform