XenonStack Recommends

Enterprise Digital Platform

Micro Frontend Architecture and Best Practices

Navdeep Singh Gill | 25 May 2023

Micro Frontend Architecture and Best Practices

Introduction to Micro Frontend

It is a Micro-service Testing approach to front-end web development. The current trend is to build a powerful and feature-rich web application that resides on top of a Microservice architecture. Over time the its Architecture becomes a part of the application, often it is developed by a separate team, grows, and gets more difficult to maintain, this type of application is called Frontend Monolith. To solve this problem, the concept of it came into the picture.
The right choice unless we examine the complexity of our frontend ecosystem. Source: Integrate dot js Frameworks to Micro Frontend

A team is cross-functional and develops end-to-end features, from the user interface to the database. It is a more friendly and less bulky one. This type of its architecture split the entire application by business domain across the entire stack. This enables front-end teams the same level of flexibility, test ability, and velocity that backend teams get from Microservices.

What are the Benefits of Micro Frontend?

Every day a new JavaScript technology is invented, and these are increasing faster than a speeding bullet. That sometimes, it can be frustrating as every JavaScript technology has its own pros and cons. And while selecting a particular technology, everyone considers the maximum benefit and minimum risk. Its best practices help to use different techniques for different services. Here are some benefits -

  • Support code and style isolation, an individual development team can choose their own technology. The development and deployment speed is very fast.
  • It helps in Continuous Deployment.
  • The testing becomes very simple, and for every small change, you don’t have to go and touch the entire application.
  • Front-end renovation - Improving new things becomes easier.
  • High Resilience and Better Maintenance.
  • Support code and style isolation.

Why Micro Frontend is important?

In the Modern Era, with new web apps, the front end is becoming bigger and bigger, and the back end is getting less important. Most of the code is its architecture. And the Monolith approach doesn't work for a larger web application. A monolithic approach to a large front-end application becomes unwieldy. There needs to be a tool for breaking it up into smaller modules that act independently. The solution to the problem is the Micro frontend. Its code is written only in pure JavaScript and any of the JavaScript frameworks used or migrated from one framework to another.

An approach for developing small services each running in its process for continuous delivery/deployment of large, complex applications. Taken From Article, Microservices Architecture and Design Patterns

Why Microservices in the Frontend is important?

When we are working on big and distributed web applications, it makes sense to build a microservice-based architecture. Using this type of architecture, the monolith team gets split to separate independent teams, which helps improve scalability, code complexity, etc., as each team works on a specific feature of the application separately.

The same concept applies to the front-end architecture, and the it will be part of the same individual team that builds the microservice backend. So, each team will own a unique business component and develop it end to end, starting from the user interface to the database layer.

A scalable frontend includes small, maintainable, independent, and customisation components that can be served independently. Click to explore about, Top 10 Scalable Frontend Project Principles

Choosing its architecture which is similar to micro-services, there are several benefits such as,

  • Technology Agnostic: Its architecture gives flexibility to the individual teams to choose the tech stack for their microservice, which improves and makes the development cycle fast with enhanced features.
  • Faster and Isolated Development and Deployment: The development process also highly improves by adopting its architecture. As with this architecture, we can have smaller independent teams that work on different features, and the development and deployment process becomes faster.
  • Individual Testing and Less Regression Issues: With isolated teams on the front end, the development, testing and deployment cycles become smoother and help build resilient applications.
  • Maintainability: The monolithic applications are bound to become large and, hence, harder to maintain. It is built on smaller parts help maintainability through the divide and conquer approach. This ensures easily testable smaller features, and the overall time for testing is reduced.
  • Scalability: With the modular and decoupled micro frontends architecture, 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. So this allows a team with different backgrounds and skills to choose the tech stack for their microservice accordingly and focus on continuous growth.

How Micro Frontend Works?

It best practices, strategies, and recipes to build a modern web application with multiple teams using different JavaScript frameworks. Micro Frontend Architecture The main Concept behind its Architecture is as follows -

Be Technology Independent

Each team should choose and upgrade the stack without coordinating with other teams. Custom elements help to hide implementation details while providing a neutral interface to others.

Isolate Team Code

Never share a runtime, even if teams use the same framework. Build an independent application self-contained. Do not rely on shared state or global variables.

Create Team Prefixes

Use naming conventions where isolation not possible yet. Namespace CSS, Local Storage, Events, and Cookies to avoid collisions and clarify ownership.

Favour Native Browser Features over Custom APIs

Instead of building a global PubSub system, use browser events for communication. If there is a need to build a cross-team API, try to keep it as simple as possible.

Build a Resilient Web design

The features should be useful, even if JavaScript unable to execute. To improve perceived performance, use universal rendering and progressive enhancement.

What are the Best Practices of Micro Frontend?

Different practices to implement it architecture-
  1. The Single SPA meta-framework combines multiple frameworks on the same page without refreshing the page, such a React, Vue, Angular 1, Angular 2, etc.
  2. Multiple single page application lives at different URLs. For shared functionality applications, use NPM or Bower components.
  3. Isolating Micro apps into Iframes using Windows. Post Message APIs and libraries to coordinate. IFrames share APIs exposed by their parent window.
  4. Different modules to communicate over a shared events bus. Each module is working on its own framework, as long as it handles incoming and outgoing events.
Component Libraries depending on the main app's stack, the different components and app sections are developed as libraries and “required” into the main app. Hence, the main app is a composition of different components.
D3.js stands for Data-Driven Documents is a front-end visualization library in java-script for creating interactive and dynamic web-based data visualizations. Source: Introduction to D3.js Library

How to adopt Micro Frontend?

This is how you can adopt its architecture and implement Microservices testing with Web components:

Integration in the Browser

Web components provide a way to create fragments of Front-End imported into Web applications. Those fragments can be packaged into Microservices together with the back-end. Services built, completed with both logic and visual representation packed together. Using this approach, Front-End applications reduced to routing make decisions involving which set of components displayed and orchestration of events between different web components.

Web Components

Web components allow the creation of reusable components imported into Web applications. These are like widgets imported into any Web page. These are currently working in browsers such as Chrome, Opera, and Firefox. If the browser does not support web components natively, compatibility is accomplished using JavaScript Polyfills. Web components consist of 4 main elements used separately or altogether 
  1. Custom Elements
  2. Shadow DOM
  3. HTML Imports
  4. HTML Templates

Custom Elements

Create custom HTML tags and elements with Custom Elements. Each element has its own CSS styles and scripts. Create own tags, apply CSS styles and add behaviours through scripts. The only standard required to put hyphen to avoid conflicts with new HTML elements. For example, create a list of check-outs, both with custom elements and custom tags, results in Web components and element lifecycle callbacks. These lifecycle callbacks allow defining behaviours specific to the component development. Life-cycle callbacks with custom elements are as follows -
  1. CreatedCallback - It defines behavior that occurs when the component got registration.
  2. AttachedCallback - It defines behavior that occurs when the component is inserted into the DOM.
  3. DetachedCallback - It defines behavior that occurs when the element is not present in the DOM.
  4. AttributeChangedCallback - It defines behavior that occurs when an attribute is added, changed, or removed.
The best Example of Custom Elements

class CheckoutBasket extends HTMLElement {

constructer (){...} is created

connectedCallback (){...}  attached to DOM

attributeChangedCallback (attr , oldVal , newVal)  someone changed an attribute

disconnecteCallback () {...}  removed from DOM, cleanup events that have been registered
}
Custom elements by default - stencil, svelte, SkateJS, AngularElements, hyperHTML Elements etc.

Shadow DOM

Shadow DOM the DOM is the API that combines HTML, CSS, and JavaScript inside a Web Component separated from the main document's DOM when these are inside a component. This separation is similar to the one user while building API services, and the consumer of an API service does not know about its internals, the only thing that matters for a consumer is API requests. Such a service does not have access to the outside world except to request APIs of other services. Similar features were presents in web components. Their internal behavior is not accessed outside, except when allowed by design, nor does it affect the DOM document they reside in. The main method of communication between web components is by firing events.

HTML Imports

For web components, HTML imports are the packaging mechanism. HTML imports tell DOM the location of a Web Component. In the context of Microservices, import remote location of service contains the component to use. HTML imports is a method to reuse and include HTML documents via other HTML documents. Predefined components as HTML imports, where each of them includes own styles and scripts, decide on the top level that HTML import presents in DOM at the moment, and the imported document handles the rest of things.
  • Shell is a top-level wrapper that consists of a container for component and component picker. It should include controllers or views which allow user manipulating components.
  • The container is the actual root place where the HTML code of nested applications should be injected. For all nested apps, it should have a single entry point.
  • Component Picker allows managing of nested applications active at the moment.

HTML Templates

The HTML template element holds client-side content not rendered when a page is loaded. Let's try to understand the implementation of it Architecture with the following React Js app example - It is good when web applications are developed independently to change to the element done without being blocked by others and breaking others. That's why in this example new react app needed to build, run, and deploy separately, treating others that have to communicate with it as services. Example: The example below creates a header for a web page. In this example, React.js used. The modern generation is using it nowadays. Let's use create-react-app for fast bootstrap -

npm install -g create-react-app

create-react-app head

cd head/

npm start
Now let’s add server-side rendering real quick. Later it will help when joining apps for SEO and performance reasons. It is easier to attach this at the beginning. micro fronted Then, create a file called server.js at the root of the project that will start an express server, and server-side render react –

const path = require('path');

const fs = require('fs');

const express = require('express');

const React = require('react');

const App = require('./transpiled/App.js').default;

const { renderToString } = require('react-dom/server');

const server = express();

server.get('/', (req, res) => {

const htmlPath = path.resolve(__dirname, 'build', 'index.html');

 fs.readFile(htmlPath, 'utf8', (err, html) => {


const rootElem = '
const renderedApp = renderToString(React.createElement(App, null));  res.send(html.replace( rootElem, rootElem + renderedApp) );  server.use(express.static(‘build’)); const port = process.env.PORT || 8080; server.listen(port, () => {  console.log(`App listening on port ${port}`);  }); This script takes root to react element (App), renders it to a string, and pushes it into the HTML before serving it to the user. React will later mount on top of that already rendered component. But, that runs on NodeJS, and Node JS doesn't understand JSX or other newer syntaxes like import, so babel used to transpile it before running the server -

npm install --dev babel-cli babel-preset-es2015
Add two tasks to the scripts section in package.json to run that -

"transpile": "NODE_ENV=production babel src --out-dir transpiled --presets es2015,react-app",

"start:prod": "NODE_ENV=production node server.js"
That's it. Now run the header with -

npm run build

npm run transpile

npm run start:prod

How to scale Frontend with its Architecture?

Now that we know why its architecture is the future of frontends and how it helps to scale our applications, we can now understand how the micro frontend architecture could be implemented. To have control over the team's code and how the work between the teams should be allocated to have a well-functioning architecture, we must know and understand the best way to split the application's codebase first. So the codebase could be split between the teams according to the vertical slices of business functionality instead of the technical capabilities. In a vertical split, we prioritize business domains and assign each domain to different teams. Since responsibility for the entire interface would be with a single team, it'll gradually help the teams to gain more expertise in that specific business domain. There are many ways to implement the its architecture, and the method you choose depends on the application's needs.

Server-side Composition

With server-side composition, a service usually sits between the browser and the application servers. One of the key benefits of using server-side composition is that the page that reaches the customer's browser is already fully assembled before. The micro-frontends are cached at CDN (Content Delivery Network) and then served to the client or the user at the build time. The server composes it in a view that is later served at build time, retrieves it, and organizes and assembles the final page. This helps increase page load speed which is significantly higher than the pure client-side integration techniques. Also, this increases the robustness of the application.

An open source platform that automates container operations and Minikube is best for testing kubernetes. Click to explore about, Java Microservices Application on Kubernetes

Build-time Integration

The build time integration could be done using web components or any component-driven frameworks. So one of the approaches is to publish it as a package and then use a container application to indulge them as the library dependencies. So this integrates the components at the build time and helps keep a fully decoupled build pipeline for each of the frontends. This helps in achieving effective collaboration and code reuse.

If we look at the package.json of the container application that uses the micro frontends as a package.

Runtime via JavaScript

The runtime Integration approach is bundling and configuring the front-end's micro frontends at Runtime. A package.json is not used to bundle the individual. One way to implement runtime integration is via JavaScript.

Runtime integration via Javascript is the most flexible approach the teams use for micro-frontend implementation. In this approach, each of it is incorporated into the page using a <script> tag. The script tag doesn't render anything immediately upon load, but they attach global functions as entry points. The container application then determines which should be mounted and calls the relevant function to tell it when and where to render itself.

manage-the-full-lifecycle-of-ml-in-production
Want to scale frontend development to make your teams work together? Contact our Microservices Experts

Runtime via Web Components

Another alternative to the approach mentioned above for runtime integration is using web components. In runtime integration via web components, each of it defines an HTML element for the container to represent an instance instead of defining a global function called by the container. Web Components are isolated components that can be (re)used in HTML pages and web applications. These are also known as custom elements.

Runtime Via Frames

One of the simplest ways to compose the application in the browser and build pages from autonomous sub-pages is the Runtime integration via iframes. Developers can get better isolation in global variables and styling with iframes. Iframes are the HTML documents that could be embedded inside another HTML document. SO they make it easy to build pages from independent sub-pages. It has been shared that Spotify uses Iframes to stitch together different parts of the view.

Concluding

 
Make sure the core and the integration are as simple as possible in its Architecture. One of the critical problems is to standardize the UI/UX principles. A universal solution is to use a style guide, for example - Material Design, Bootstrap, among others. Communication with the team is the key to ensure everything is running smoothly, create some standards and rules to minimize conflicts with the difference of teams working on a product. All these Micro Frontend Architecture best practices help to solve one problem, scalability. An application that tends to grow significantly introduces numerous issues and conflicts, splitting the code into teams and applying the right logistics to deliver good quality, technology trending, and fast solutions to the world.