Table of Contents
Introduction of Micro Frontends
How to Implement Micro Frontends
Communication between Micro Frontends
Every day new frontend technology is invented and increasing faster than bullet speed. Sometimes it's frustrating as every frontend technology has pros and cons. and we mostly consider a technology that has minimum risk and maximum benefits. But What if we can use different technologies for different services!!
In modern web apps, the frontend is now getting bigger and bigger. In this scenario, the frontend becomes more complex and unmanageable. What if we can scale frontend without making it complex, easy to manage, and with even more benefits?
In this article, I will describe a recently trending architecture micro frontend that helps to scale frontend effectively for large-scale applications. As well as I will share my knowledge and experience of what micro frontend is about, and how it works, also we'll cover some of the implementation approach, and key points and we'll dive deep into a full example application that demonstrates the technique.
Introduction of Micro Frontends
What are Micro Frontends?
Micro frontend architecture concept inspired by microservices. where we break frontend monoliths into smaller pieces that can increase the effectiveness and efficiency of teams working on frontend code.
Each team handles a specific part in which they are specialized in. micro frontend provides a new approach to building and scaling frontend apps. It also solves the key obstacles that companies have faced at both technical as well as organizational levels.
Why Use Micro Frontends?
Technology Flexibility: Micro frontend architecture allows individual teams to choose and update a tech stack for their particular part.which improves development speed and feature enhancement.
-
Scalability: In micro frontend architecture, we break down applications into small pieces based on business logic or domains. Each team works independently on multiple systems. We can scale up an application to multiple teams as a new frontend element or changes to the existing frontend would not affect the rest of the frontend and other team's work.
-
Isolation over Development and Deployment: each team is isolated; they develop, test, and deploy their frontend part without depending on other teams. This makes the process smoother and faster.
-
Maintainability: on a large scale applications become hard to maintain. Hence micro frontend architecture makes it easy here because it divides everything into small parts so each team is responsible to maintain their part. Sometimes if any micro feature or part goes down it doesn't affect the rest of the app.
When to use Micro Frontends?
We saw what micro frontends are but it's not a solution to all problems. it's essential to understand when to use micro frontends
Micro frontends architecture is a technique that makes scaling projects easier. When a few people are working on the application, scaling is probably not an issue. but working on big and distributed applications, where team size is large then it makes more sense to use micro frontend architecture.
If you need to create a lot of different apps and native user interfaces to run on every device, that might also become tricky for one team to handle. The micro frontend is quite popular in the e-commerce sector but it is also used in other industries.
Read More: How To Create NPM Packages for React Native
How to Implement Micro Frontends
Build Time Integration
In this approach, the container will install components as a library like npm packages. This helps to achieve code reuse and collaboration. but the issues with this approach are syncing different versions and build issues. we have to deploy it every time it has changed or issue. also, it will increase the size of the package because of contained dependencies.
Run Time Integration
Run time integration has 3 types of composition:
- Server side composition
- Edge-side composition
- Client-side composition
Server side composition
In this composition, the backend decides which micro frontend to load. the server will determine what URL to route the request to. a service usually sits between the browser and the application server. The benefit of server-side integration is that the page is already assembled when it reaches the customer's browser. We can achieve incredibly good page load speeds that are hard to achieve using client-side integration.
Edge side composition
Edge-side composition assembles the view at the CDN level, retrieving your micro-frontends from the origin and delivering the final result to the client. Many CDN providers allow you to use Edge Side Includes (ESI), an XML-based markup language. ESI was introduced to support the possibility of scaling a web infrastructure by exploiting a large number of points of presence around the world provided by a CDN network. However, ESI is not implemented in the same way within each CDN provider, which complicates its usage.
Because transclusion at the CDN level doesn’t make sense with vertical splitting, this approach only works with horizontal splitting. Edge-side composition is most useful on static pages that have to scale. The CDN takes care of the scalability aspect of the final solution and will serve the user from the closest point of presence possible.
Client side composition
In this architecture pattern, an application shell loads multiple micro-frontends directly from a CDN (or from the origin of the micro-frontend is not yet cached at the CDN level), often with JavaScript or an HTML file as the micro-frontends entry point. This way, the application shell can dynamically append the DOM nodes (in the case of an HTML file) or initialize the JavaScript application (if the entry point is a JavaScript file).
It’s also possible to use a combination of iframes to load different micro-frontends. Otherwise, we could use a transclusion mechanism on the client side via a technique called client-side include, which involves lazy loading components inside a container using a placeholder tag and parsing all the placeholders to replace them with the corresponding component. Client-side composition works well for both vertical and horizontal splitting and is particularly useful when you have highly skilled frontend teams who may be less keen on or less experienced with full-stack solutions. I suggest using this pattern in environments in which you have control over the final targets: desktop applications, enterprise applications, or web platforms with a strong client-side component.
Communication between Micro Frontends
One of the most common questions regarding micro frontends is how to let them talk to each other. I recommend having them communicate as little as possible. but Sometimes we require communication between micro frontends like sharing a state, logic, or component. We always want to notify other micro-frontends about the user interaction when we work with multiple micro-frontends on the same or different pages. For both horizontal and vertical approaches, think about how views communicate when they change even if it also depends upon the type of composition. If two micro frontends frequently state each other, consider merging them.
Sharing a state
Eventually, there are quite a few ways to communicate
-
Pub-Sub Library (Windowed Observable): A microservices environment is a pretty much popular communication mode is pub/subs. Since every micro frontend and the container are at the window, I decided to use the window to hold a global communication using a pub/sub implementation, Pub/Sub enables you to create systems of event producers and consumers, called publishers and subscribers. Publishers communicate with subscribers asynchronously by broadcasting events.
-
Web storage: As long as all the micro-frontends are in the subdomain, we can store and access data through web storage like cookies, sessions, and local storage.
-
Query String: A query string is the least efficient way, but still, we can use it in scenarios where we can pass data through query strings and access them in micro-frontends.
-
Custom Events: Custom events allow micro frontends to communicate indirectly, which is an excellent way to minimize direct coupling, though it does make it harder to determine and enforce the contract that exists between micro frontends.
Sharing a code
Functions, components, and common logic could be placed on a third package and imported into each app.
And for creating a package there are several approaches I won't dive deep into it, but I'll leave you some examples:
Checkout this link to make its library for sharing code. Creating a simple typescript library
Routing in Micro Frontends
As with compositions, there are three ways to route your micro-frontends: server-side, client-side, or edge-side.
Server-side routing, in which a web server returns different static assets based on the path requested, is a pretty standard approach. You can also add parameters in a query string that will be used by the client for communicating between micro-frontends. This orchestration technique can be used with either a vertical or horizontal split.
Client-side routing, in which an app shell contains and loads the micro-frontends, is mainly used when you’ve sliced your applications vertically so that the app shell loads one micro-frontend at a time instead of a multitude at once. If you’re using a horizontal split, you should have a page—a container like an app shell—that contains the different micro-frontends. By changing the URL, you can load a different page with a different view.
Edge-side routing, which leverages the latest features of some CDN providers to run code on the edge that orchestrates and returns the static files using a web server, means you don’t have to deal with scalability problems. However, the code running on the CDN should be quick to execute, which can be a limitation in some cases. At the same time, you can return your static assets more quickly than if you were relying on a standard web server implementation, especially considering that the points of presence are closer to the user's request.
Styling
CSS as a language is inherently global, inheriting, and cascading, traditionally with no module system, namespacing, or encapsulation. Some of those features do exist now, but browser support is often lacking. In a micro frontends landscape, many of these problems are exacerbated. For example, if one team's micro frontend has a stylesheet that says h2 { color: black; }, and another one says h2 { color: blue; }, both these selectors are attached to the same page, then someone is going to be disappointed! This is not a new problem, but it's made worse by the fact that these selectors were written by different teams at different times, and the code is probably split across separate repositories, making it more difficult to discover.
Over the years, many approaches have been invented to make CSS more manageable. Some choose to use a strict naming convention, such as BEM, to ensure selectors only apply where intended. Others, preferring not to rely on developer discipline alone, use a pre-processor such as SASS, whose selector nesting can be used as a form of namespacing. A newer approach is to apply all styles programmatically with CSS modules or one of the various CSS-in-JS libraries, which ensures that styles are directly applied only in the places the developer intends. Or for a more platform-based approach, shadow DOM also offers style isolation.
The approach that you pick does not matter all that much, as long as you find a way to ensure that developers can write their styles independently of each other, and have confidence that their code will behave predictably when composed together into a single application.
Deep dive into the Example Application
After learning about a micro-frontend, we are jumping into an example where we are trying to create micro frontends using Vue.js. to use a Vue component in the application we use webpack 5's Module Federation to share and consume Micro frontend components.
Scenario
Here, We create two applications. The first one is a container, and the second one is home. The container application is an entry point of the user and we add home as a micro frontend component. Let’s get started.
Create a Vue app named container and home
To create a Vue project first you need to install node and npm inside your system. Then install Vue-CLI using this command:
npm install -g @vue/cli
After installing a CLI, we will create a Vue project using this command. I suggest going for the default option of Vue3.
vue create {{Project name}}
We will create two projects here named Container and Home. replace {{Project name}} with your project name.
Creating a mono repository
Once we create a project we will put both of them inside the same folder. Now using an npm workspace feature we will create monorepo which helps us to manage both projects in a single repository. To do that first we will make a package.json file.
Here we are mentioning the project name inside a workspace and after that do npm install to install node modules.
Setup a container application
First, Let's start with the container app. A container is a host here so first, we make a basic structure inside the app.vue file.
The next thing we will configure is our webpack file. We will add module federation configuration inside the plugin section. This thing helps us to act as a host and load micro frontend inside our application.
Now it's time to register components inside our main.js file which helps us to use our micro frontend inside the template part.
Setup a home application
Now, We will set up our home application. A home application is remote so we will make one file called header.vue file which contains a simple layout that will be loaded inside the container application using a module federation. Here it's our header.vue file
After creating a header file we will update our webpack file and configure module federation. You can see we are exposing our component to the host for access. Here it’s a webpack.config.js file
It's time to run our project as mentioned above in the scenario. We can see that the home application is loaded inside the container application.
Conclusion
These are just a few of the considerations and challenges you’ll encounter when implementing micro-frontends. While micro-frontends offer the benefits of more independent teams, autonomous deployment, and faster innovation, I suggest investing in understanding your business needs, ensuring you are crystal clear on what you need to build, and then going ahead and embracing the micro-world.