Site icon Techolac – Computer Technology News

Which Technique/Architecture is right for my Project?

From Micro Frontends in Action by Michael Geers

This article covers:

You can save 40% off Micro Frontends in Action, as well as of all other Manning books and videos.

Just enter the code psfrontend20 at checkout when you buy from manning.com.

[Find out more]

In this article, we go over the terminology and highlight the key advantages of different techniques and architectures. After that, you’ll learn about the Documents-to-Applications Continuum. It’s a concept that helps you with the decision of whether to render your markup on the server, client, or both. This distinction is crucial because it determines which architectures and integration patterns are suitable for your use-case. We’ll end with an architecture decision guide. You’ll learn how you can make a sound choice based on a handful of questions. These questions lead you through the different options.

Revisiting the terminology

When you’re setting up a micro frontends project with different teams, everyone must use the same vocabulary. This is why we take a moment to sort the terms. We’ll start with the basic building blocks: the integration techniques. Then we’ll look at different high-level architectures that you can build with them.

Figure 1 shows several integration techniques.

Figure 1. The integration techniques required for a micro frontend architecture. On the left, we see two techniques for handling cross-team page transitions. The right side shows a list of methods to compose different user interfaces onto one page.

We can group them into two categories: Page-Transition and Composition. Let’s look at the transitions first.

Page transitions

When we talk about page transitions as an integration technique, we mean inter-team page transitions. How does a user get from a page owned by Team A to a page owned by Team B? From an architectural standpoint, it’s unnecessary to know how a team handles transitions between its pages. This is an implementation detail.

Links

The plain old hyperlink is the most basic form for doing a micro frontends integration. Each team is responsible for a set of pages. Handing over the user to another part of the application is as easy as placing a link to the other team’s work. In its cleanest form, there’s no extra coordination needed. Teams could even host their part of the applications under different domains.

App shell

Clicking on classical hyperlinks forces the browser to fetch the target markup from a server and then replace the current page with the new one. Having a reload is fine for a lot of use cases. But the evolution of the browsers History API and the rise of single-page app frameworks enabled developers to build entirely client-side page transitions. Its main benefit is the opportunity to render the layout for the target page instantly. That way, the user gets a quick response, even if the content data requested from the server is still pending. Implementing client-side page transitions across team boundaries requires a central piece of JavaScript in the browser. It’s often called app shell. The central app shell acts as a parent application to the single-page applications built by the different teams. It determines which team-applications should be active based on the browser’s URL. When the URL changes, it passes the responsibility for the page from Team A to Team B.

Composition techniques

In practice, you often want to show user interface parts from different teams on one page. A typical example of this is a header or navigation micro frontend. One team builds and owns it. All the other teams integrate it on their page. It could also be functionality like the buy button or mini basket on our product page.

In this article, we call an includable micro frontend a fragment. To make the integration happen, we need a shared format. The owner of the fragment must provide it in a standardized format. The fragment consumer uses this format to integrate the desired micro frontend on his page.

We can broadly group the composition techniques into two buckets: server-side integration and client-side integration. We’ve also included the iframe and AJAX techniques because they’re a hybrid between server and client.

Server-side integration

Implementing a server-side integration technique makes sense when teams generate their markup server-side. The markup for all fragments of a page gets assembled before it reaches the customer’s browser. A central piece of infrastructure, like a web-server, performs the markup assembly. You can use the SSI technique in the NGINX to perform this task, but an alternative approach is the team which owns the page fetches the required fragments directly from the other teams. The server-side integration libraries Tailor and Podium work like this.

Client-side integration

If teams generate their markup in the browser, you need a client-side integration technique.

The most popular approach is using the Custom Elements API from the Web Components spec. The API defines (de)initialization hooks. The team that owns the fragment implements them. This way, the integration happens directly through the browsers DOM API. No special libraries or custom JavaScript APIs required.

An essential part of client-side integration is communication. How can fragment A inform fragment B about an event that might be interesting? Micro frontends can communicate via Custom Events or an event-bus/broadcasting solution.

iframe

The iframe is the weird but somewhat powerful stepchild of web development. It fell out of favor years ago for various reasons. Using iframes in responsive design doesn’t work without JavaScript, and having a lot of iframes on a site is resource-intensive, but its secret superpower is that it provides a high level of technical isolation. In a micro frontend context, this is a desirable feature. This way, faults in micro frontend A can’t negatively affect micro frontend B. Communication across iframes is also possible through the Window.postMessage API.

AJAX

Fetching a snippet of markup from a server-endpoint via JavaScript is the technique which enabled the Web 2.0 revolution back in the days. You can also use AJAX as an integration technique for micro frontends. Client-side JavaScript triggers the AJAX call to fetch server-side generated HTML. AJAX is a bit of a hybrid approach which doesn’t fit into one of our client-side or server-side integration buckets. It’s often used in tandem with a server-side integration technique – incrementally updating the markup of an embedded micro frontend. It doesn’t come with a canonical way to handle (de)initialization and communication. Using Web Components together with AJAX for internal updating, is also a good fit.

These are the basic integration techniques you’ve learned. Let’s zoom out a bit and have a look at different architectural styles.

High-level architectures

One of the micro frontends benefits is that teams are free to use the technology that fits their slice of the application best. Before you start setting up a micro frontends project, all teams need to be on the same page when it comes to the high-level architecture. Are we building static pages that integrate solely via links, or is the goal to create a highly dynamic and tighter integrated single-page app? You should consciously make this decision together with all teams. As always, there are no silver bullets and no right and wrong solutions, but having a shared vocabulary and a common understanding of what you are working towards helps everyone. It also eliminates misunderstandings down the road.

Figure 2 shows six different high-level architectures.

Figure 2. Different architectural styles to build a micro frontends project. This chart starts with the simplest form, the linked pages approach, and shows how you can extend this with extra features like single-page applications, universal rendering, or a shared application shell.

We’ll go through them from top to bottom.

Linked Pages

Every team serves its pages as complete server-rendered HTML documents. Clicking a link reloads the complete page and shows the desired content. This hard navigation happens if you move between pages from the same team or if you navigate across team boundaries. The simplicity of this approach is its main benefit: no central infrastructure or shared code are required, debugging is straight forward, and new developers instantly understand what’s going on. From a user experience point-of-view, there’s room for improvement.

Server Routing

It’s identical to the Linked Pages approach, but with one difference; all requests pass through a shared web-server or reverse proxy. This server sits in front of the team’s applications. It has a set of routing rules to identify which team should handle an incoming request. The routing is often done via URL prefixes associated with a specific team.

Linked SPAs

To improve the user experience and react to input faster, a team can decide to switch from delivering static server-generated pages to implementing a client rendered single-page app for the pages they own. This way, all link clicks for pages of this team result in a fast-soft navigation. Transition between team boundaries are still hard navigations. Technically the adoption of a single-page app architecture inside one team can be seen as an implementation detail. As long as linking to a specific page from the outside still works, teams can decide to change its internal architecture. The distinction between Linked Pages and Linked Single-Page Apps is essential when we talk about more advanced architectures and suitable integration techniques later.

Linked Universal SPAs

Teams can also decide to adopt universal rendering. The markup for the first request gets rendered on the server. It enables a pretty fast first page-load experience. From this point the application behaves like a single-page app—incrementally updating the user interface as needed. From a team’s point of view, this is a more complicated setup, which requires some additional development skills. From an architectural view, this approach is identical to the other “linked” architectures. The contract between the teams is still a set of shared URL patterns. A navigation across team boundaries results in a reload of the page. But when

implemented well, these reloads should be more seamless compared to a Linked SPA architecture, where the browser needs to execute a bunch of JavaScript before the user can see the content.

Unified SPA

The Unified SPA describes a single-page application composed out of other single-page applications. It requires all teams building their software as a single-page app. These single-page apps are then unified by a parent application, which is often called the application shell. The shell typically doesn’t render any user interface. Its job is to listen to changes in the browsers address bar and pass control from one single-page app to another if necessary. With the Unified SPA architecture, all page transitions are soft navigations. This leads to a snappier and more app-like user interface. The app shell is a central piece of code. It introduces a non-trivial amount of coupling and complexity.

Unified Universal SPA

When we take the Unified SPA model and introduce universal rendering, we arrive at something we call Unified Universal SPA. With this model, each team builds a single-page app with universal rendering capabilities. To make this work, the parent application (app shell) also needs to be universal. It needs to be able to run on the server and the client. This is a pretty challenging architecture. It promises to combine the best of all worlds but comes with the most complexity.

Comparing complexity

Building a product by combining user interfaces owned by different teams comes with a considerable amount of complexity. A large part is organizational:

The architecture and level of integration you choose has a considerable effect on your complexity. This complexity manifests itself in different aspects:

Figure 3 sorts the described architectures in four complexity groups. Starting with simple and ending with complex.

Figure 3. Micro frontend architectures sorted by complexity. The Links & Pages approach is the simplest one to build and run. The complexity arises as you move to more sophisticated architectures. The Unified Universal SPA approach requires a lot of development skills to get it right. You also need shared infrastructure and code to make it happen.

This is of cause only general guidance. The real cost associated with an architecture depends on your team’s experience and the use-case. As a rule of thumb, you should always opt for the simplest architecture you can responsibly get away with.

Sure, it’s nice to have a Unified SPA with no hard page transitions, but does the extra work required to achieve and maintain this justify the potential benefits?

Heterogeneous architectures

In the descriptions above, we always assumed that all teams use the same architecture, but you can also mix and match. For a team that builds fast-loading landing pages, the Links & Pages approach might be sufficient, but for a seamless browsing experience, you want to create a Unified SPA that integrates the team which owns the product list and the team managing the product pages. These architectures can work side-by-side: Some teams are doing Links & Pages, and others share an application shell to deliver a Unified SPA.

This way, you only increase the complexity in the areas where it’s needed.

Having a heterogeneous architecture also has drawbacks:

Are you building a site or an app?

As you’ve seen in this article, it makes a significant difference if you render your markup on the client or server. It’s a general question that everyone who’s setting up a new web project has to answer, but in a micro frontends context this decision is essential. It defines which integration techniques are suitable.

In this section, you’ll learn about the Documents-to-Applications Continuum. I’ve found this concept helpful in architecture discussions. It creates an excellent mental model that helps you pick the right tools and techniques for the job. It provides a counterweight to the “Let’s use the hot new JavaScript framework!”-reflex many developers (me included) have when they’re confronted with a greenfield project. After explaining the concept, we’ll look at how the high-level architectures fit into this continuum.

The Documents-to-Applications Continuum

In 2013 Aral Balkan wrote a blogpost [1], which starts with the question if the terms web site or web application provides any meaningful distinction. In day-to-day life, these terms are often used interchangeably. No unique technology magically transforms a web site into a web application. A difference arises when we look deeper. What purpose does the project we’re building serve? Do people come to our site to consume content, or

do they want to use a specific functionality we provide? For better visualization, it helps to look at extreme examples:

The first one is a prototypical web site where the content is essential. The second one is a pure application. It doesn’t bring any content. It’s all about the functionality it provides to the user.

In a non-trivial project, it’s typically not black and white. This is where the Documents-to-Applications Continuum comes in. The idea is that both examples are at different ends on a spectrum, as illustrated in figure 4.

Figure 4. The Documents-to-Applications Continuum provides a mental model to help you think about if your project is more of a web site or a web application. It’s a gradual scale and not a black or white decision.

Let’s look at two examples. Where does amazon.com fit on this scale? They provide a lot of functionality. You can search, sort, and filter through product lists, rate products, manage your returns, or have a live chat with their customer service. At its core, it’s a content-centric site. A good question to ask is: Would the site still be useful if we’d strip away all behavior? For amazon.com, we can answer this with a definite yes. No doubt, the extra functionality’s also important, but without products, the features would be useless. We’d put their site somewhere on the left part of the continuum.

Now to our second example. The site CodePen.io let’s web developers and designers put together HTML, CSS, and JS to get a live preview in the browser. Developers use the online code editor to sketch out ideas or isolate bugs. CodePen also has an active community of people who showcase their work and share code with others. You can go to the site and discover new exciting techniques by browsing the public catalog. How does CodePen fit on our continuum? It’s a harder question to answer because its strong on both aspects: the online editor (behavior-centric) and the public catalog (document-centric). If we’d strip away all behavior, the online editor would vanish. If we remove all content, the catalog disappears, but the editor is still there. This is why we’d probably put CodePen in the middle of the spectrum.

Server, client or both

Classifying your product onto the continuum is a good starting point to identify if your templating should live on the server or in the browser. If your product has a strong content focus, server-side rendering should be your first choice. Using progressive enhancement to add functionality should feel natural.

If you’re building an application where it’s all about interaction and not about content, a purely client-rendered solution is the best fit. Here the concept of progressive enhancement doesn’t help you at all because there is no enhanceable content.

For a project that resides in the middle of the spectrum, you need to make a choice. Server-side and client-side templating are valid options, but in this area, both have their advantages and disadvantages. If you aren’t afraid of the extra complexity, you can also pick both and go with the universal rendering option.

Let’s revisit our high-level architectures. Figure 5 highlights which of them use server-, client- and universal rendering.

Figure 5. Illustrates which architectures feature server-side, client-side or universal rendering.

Make sure the architecture you choose aligns with the nature of your project and the business. Templating and complexity considerations are two significant factors in making a decision. Next up, we’ll take another angle on this decision using a decision tree.

Picking the right architecture and integration technique

Now we’ve sharpened our vocabulary and have a mental model to pinpoint what kind of product we’re building. Let’s look at a concrete way to determine which architecture and integration your project needs. Figure 6 shows a decision tree that helps with this question. It’s inspired by Manfred Steyer’s work [2] for creating Angular based frontend microservices.

Take some time to understand what’s going on in this diagram. Follow the lines from top to bottom by answering the questions until you reach your high-level architecture. From there on, you can follow the dotted line to get to the compatible Composition Technique. If your use-case doesn’t require you to have different micro frontends to be active at the same time (fragments or nested micro frontends), you can skip this step.

Figure 6. The decision tree helps to pick a micro frontends architecture based on your project requirements. It also shows which kind of composition technique is appropriate for your use-case.

What does your project need?

Let’s have a look at the questions in this decision tree.

Strong isolation (legacy, 3rd party)

Do you want strong technical isolation between the code of the teams? Why wouldn’t you? Isolation and encapsulation generally lead to less unforeseen effects an reduces bugs. Sadly, opting for strong isolation eliminates a lot of other possibilities. The right question to ask is: do you need strong isolation? This is typically true if you integrate a legacy system which doesn’t respect namespacing rules and requires global state to work correctly; another reason is security. If you’re integrating with an untrusted third-party solution or one part of your application has high-security requirements (e.g., it handles credit card data), it can be necessary to better shield the micro frontends against each other.

Fast first page load / Progressive enhancement

This is a double question. If you need one of these properties, you should follow the yes arrow.

Having a fast first page view is always pleasant, but the importance of this property heavily depends on your business. If you want your site to rank high in search results, first page load performance is nothing you can ignore. Search engines like Google increasingly favor fast-loading sites in their ranking [3]. Even if search ranking isn’t your primary goal, there are a lot of case-studies [4] that show how better web performance increases business metrics.

If you’d locate your project on the middle or left side of the Documents-to-Applications Continuum, I’d highly recommend adopting progressive enhancement practices. You should encourage all developer teams to learn about this approach. For developers who started their web career with frameworks like React or Angular, the concepts might sound strange at first sight. Architecting features with progressive enhancement in mind and embracing the primitives of the web leads to more maintainable, easier to understand, and more stable software. If you’re on the far right in the continuum and building a pure web application, there’s typically no content to enhance.

Then progressive enhancement won’t help you at all.

Instant user feedback

In the last question, we talked about the first page load performance, but how does your site react to further interactions from the user? The classical “click a link” and “fetch generated markup from the server” works for a lot of cases, such as when you use AJAX techniques to avoid a full page reload. In this model, the complete templating resides on the server. This means at least one server-roundtrip is necessary to update the UI in response to a user input.

If you need to be faster than this, you must adopt client-side rendering. Fetching data is a little bit faster because JSON data is more compact than rendered HTML, but the network latency stays the same. The most significant advantage of client-side rendering is that it enables us to provide instant feedback. Even if the data the user wants to see is still in transit, you can update the view and show placeholders and skeleton screens.   [5]

It also enables you to adopt Optimistic UI patterns. [6] With Optimistic UI, you try to increase the perceived performance by instantly rendering the result which is most likely. Let’s look at a shopping cart example: When a user wants to delete an item, she clicks on the delete button. The browser calls the associated API on the server, and when it comes back, the item is deleted from the visual shopping cart list. With Optimistic UI, you assume that the delete API call works in most of the cases. This is why you remove the line item directly and don’t wait for the API call to return. If this assumption turns out not to be true (item remove failed), you restore the item in the user interface and show an appropriate error message. This technique is powerful, but because you are effectively lying to your user, you should use it with care.

Being able to render a response to a user input instantly improves user experience and makes your site feel more app-like.

Soft navigation

In the “instant user feedback” question, we talked about improving the user experience inside a micro frontend. Now let’s look at what happens when the user transitions across team boundaries. This question differentiates the linked architectures from the unified architectures. How important is it that inter-team page transitions are client-side rendered?

Answering this question depends heavily on the team boundaries you establish, the number of teams and the usage pattern of your application.

If you create your team boundaries along with the user’s tasks and needs, they don’t cross team boundaries that often.

Say you’re building a website for a bank. It has to distinct areas developed by two teams: users can check their account balance (Team A), and they can also calculate and request a housing loan (Team B). For a good user experience, it might be essential to provide a high amount of interactivity inside these areas. This is something Team A and Team B can decide independently,but because users seldom switch between balance checking and loan requesting in one session it might be fine to have a hard navigation between these areas.

Let’s pick another example. We’re building a call-center application. The agents use the application to manipulate orders (Team A) and make personalized recommendations (Team B). Because the agent switches between these two micro frontends frequently, it might be a good idea to implement soft-navigation. It makes using the application faster and positively impacts the agent’s workflow.

Multiple micro frontends on one page

If you’ve answered all questions on your way down the tree and arrived at your high-level architecture, there’s one last bonus question: “Do you need composition?” Answering “yes” brings you straight down to the associated composition technique. If you’re building a pure SPA, you need to integrate client-side. If you opt for server-generated pages, you should use a server-side integration.

Having a composition technique is optional. When we look at our banking example from the section before, we might not need a composition technique. The account area and the housing loan area could be two distinct sections of the site that link to each other.

The most common example of a composition is a header and navigation fragment. Usually, one team owns it, and the others include it on their page.

Basis for all teams

Most companies adopt micro frontends to increase their development speed. Creating a structure where teams can work side-by-side without having to discuss and coordinate with other teams. All teams need to build against the chosen architecture and be compatible with the integration mechanism.

As stated earlier, you should try to pick the most straightforward architecture you can get away with. Avoid central infrastructure where possible. Major changes in central infrastructure are often hard to make when they require an action from all teams.

Summary