{{announcement.body}}
{{announcement.title}}

Introduction to Micro-Frontend Architecture

DZone 's Guide to

Introduction to Micro-Frontend Architecture

In this article, we discuss some basics behind micro-frontend architecture to help you understand an option to better manage your application.

· Microservices Zone ·
Free Resource

As the frontend world grows, the architectural solutions for it should grow as well. The main idea behind micro-frontends is to divide frontends into independent parts so that independent teams can maintain them.

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

Traditional frontend architectures

Traditional frontend architectures

Microservices Advantages

  • Decoupled codebase.
  • Autonomous teams.
  • Technology and framework agnostic.
  • Independent deployments.
  • Scalability.
  • Reusability.

Micro-Frontends

Micro-frontends have the same advantages as microservices do with just backends. Teams just become end-to-end.

Micro-frontend architecture

Micro-frontend architecture

Because you can use different frameworks and technologies it does not mean that you have to.

When to Use Micro-Frontends

  • Huge code base that different teams are contributing to.
  • Code ownership gets messy.
  • Deployment is delayed because of other parts of the application.
  • You want to use different FE frameworks.

Orchestration

With orchestration, we can combine different micro-frontends into one functioning application. It can be done on both the server and client-side. For maximum performance, we should combine these approaches.

There is also one more solution for this, called the Build time integration.

Build Time Integration

  • Every micro-frontend app represents an npm package.
  • The main app (orchestrator/container) builds itself into a final bundle with all dependencies (micro-frontends).
JavaScript


This seems to make sense, but it is not recommended because it has one big drawback. Every time one of the micro-frontend app changes, the whole orchestrator, with all its dependencies, should rebuild and create a new release. This can cause delays, rollbacks, or bugs for every micro frontend, and that is what we like to avoid. Also, every team should have dependencies on the same package versions, which can really make it harder to create new releases. 

1. Client-Side Orchestration

  • Client routing.
  • state sharing.
  • Register all applications.
  • Resolve shared dependencies if any.
  • Initialize the main app.
  • Compose fragments from different micro-frontend apps.

You can accomplish this list with different libraries, like:

The fragment composition can be done with a simple Ajax call for different APIs that can return pre-rendered HTML, which can be hydrated on the frontend, or only returns the needed script tag and specific HTML tag with an ID where the loaded fragment can render itself.

Also, there is a possibility to implement orchestration on your own using vanilla JS or another framework.

1.1 Routing

  1. Use the History API to initialize in-app routers.
  2. Custom browser events or PubSub library.
  3. Leave the routing on the Orchestrator app.

1.2 Sharing Global State and Communicating Between Apps

  1. Observables pattern with exported public state of each micro-frontend — RxJS is useful in this case.
  2. Custom browser events.
  3. Cookies, session, or local storage.

1.3 Shared Code — Mainly UI Library

  1. When selecting a third-party library, you should choose one that supports all used frameworks in your micro-frontends.
  2. In case if you are developing your own library, you can use Web components to make them universal.
  3. The responsibility of developing and maintaining this library should be on every team. Do not create a specific team for this purpose

1.4 Styles Collision

  1. Determine specific prefix for CSS classes for every team.
  2. Use BEM style.
  3. Use CSS and JS libraries that helps you avoid collisions — JSS, styled-components, etc.
  4. Shadow DOM from Web components.

1.5 SEO and UX

  1. Skeleton UI — a predefined splash screen for not yet loaded content.
  2. Server-side rendering with the help of ESI or SSI (described in server-side orchestration).

1.6 Web Components to the Rescue

They are defined by 4 specifications:

1.6.1 Custom Elements

The Custom Element API lets you create fully-featured, custom HTML elements with lifecycle methods, attribute change handlers, event handlers, etc.

The process of creating a custom element:

  • Create a class that extends the HTMLElement class.
  • Define your lifecycle methods, custom properties, etc.
  • Associate the new element with an HTML template (inside connectedCallback() lifecycle method).
  • Register this element using the Custom Element API.
  • Use this element in your HTML.
JavaScript


1.6.2 Shadow DOM

JavaScript


The attachShadow method takes only one parameter, which is an object with one property, mode. It allows you to create isolated DOM trees (scoped styles, self-contained components) with two modes, open and closed.

Open means that you can access the shadow DOM using JavaScript written in the main page context. On the other hand, closed means that the shadow DOM can be accessed only with the Javascript from the custom elements context. This is really useful when you have to isolate your CSS.

JavaScript


1.6.3 ES Modules

Import/export JS modules, nothing else.

1.6.4. HTML Templates

With HTML templates (<template>), you are able to create HTML fragments that are not rendered at load time, but you can initialize them at runtime with JavaScript.

HTML


JavaScript


Another useful element is <slot>. It is part of Web components technology and is used as a placeholder inside a web component that you can fill with your own markup.

HTML


JavaScript


The result is :

Output template

Output template

The defined span element with the attribute slot gets rendered inside the slot element with the attribute, name, whose value matches the value of the slot attribute on our span element.

2. Server-Side Orchestration

  • Server routing with proxying requests.
  • Register all applications.
  • Resolve shared dependencies if any.
  • Serving and composing fragments from different micro-frontend apps.

2.2. Bootstrap Application

We can call the solution for server-side orchestrationa a Bootstrap application. It can be done in different ways, and usually, it is more complex, and it consists of more than one solution. I am going to describe how some larger companies are doing this.

2.2.1 Zalandos Solution

Zalando logo


It is called the Project Mosaic9 https://www.mosaic9.org/.

The use case is as simple as you can see in the picture. The user comes to the page, and the browser hits the router, which decides if it is an API call or layout call. In the case of an API call, the router proxies the request to the needed API. In the case of a layer call, the router calls the Layout service, which knows about all possible layouts, and it loads them from different endpoints.

As you can see, in the following picture, they have created open-source projects to accomplish all of the described steps.

Micro-frontend workflow

Micro-frontend workflow

2.2.2 Facebook's Solution

They call it BigPipe. It is similar to Tailor.js from Zalandos' solution. Actually, Tailor.js was inspired by facebook’s BigPipe. The original post where you can read about how it works is here: https://www.facebook.com/notes/facebook-engineering/bigpipe-pipelining-web-pages-for-high-performance/389414033919/

2.3 Fragment Composition Possibilities

Now, let's talk about the technical side of the fragment composition on the server-side. There are two older technologies with which we can easily accomplish the composition (Server side include (SSI) and the Edge side include (ESI)). They are used to combine different HTML markups into one. In both cases, we need to maintain a map of URLs that correspond to static HTML files.

2.3.1 Server-Side Include (SSI)

In the main HTML file:

HTML


Inside the Nginx config:

Java


2.3.2 Edge side include (ESI)

  • https://www.w3.org/TR/esi-lang.
  • Small markup language.
  • It's only a proposal and is not a standard.
  • Supported by different technologies or libraries (Nginx, Varnish, etc.).
  • For NodeJS, there is the nodesi npm package.
HTML


2.3.3 Own Implementation

You can implement your own parser or some kind of tag helper on the server-side. Almost every templating library has some possibilities to implement custom tags resolving.

Conclusion

Choose your tool by analyzing the problem. Don’t try to solve every problem with your favorite tool. If you choose micro-frontend architecture you can target two groups:

Total Independence

  • Each team chooses its tech stack — no code sharing.
  • Each fragment makes its own API calls.
  • Every view is composed of fully functional fragments.
  • Each micro-frontend app has its own CI/CD.

Strategic Collaboration

  • Agree on tech stack and share common libraries.
  • API calls flow through bootstrapapp.
  • Shared UI library.
  • Shared CI/CD.
Topics:
microservice architecture ,web developement ,microservices

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}