2 My first micro frontends project

This chapter covers:

  • Building the micro frontends example application for this book
  • Connecting pages from two teams via links
  • Integrating a fragment into a page via iframes

Being able to work on a complex application with multiple teams in parallel is the essential feature of micro frontends. But the end user of such an application does not care about the internal team structure. That’s why we need a way to integrate the user interfaces these teams are creating. As you learned in chapter 1, there are different ways of assembling separate UIs in the browser.

In this chapter, you’ll learn how to integrate UIs from different teams via links and iframes. From a technology standpoint, these techniques are neither new nor exciting. But they come with the benefit that they are easy to implement and understand. The key point from a micro frontends perspective is that they introduce minimal coupling between the teams. No shared infrastructure, libraries, or code conventions are required. The loose coupling gives the teams the maximum amount of freedom to focus on their mission.

In this chapter, we’ll also build the foundation of our example project The Tractor Store. We’ll expand on this project throughout the book. You will learn different integration techniques and their benefits and drawbacks. Spoiler alert: there is no “gold standard” or “best integration technique.” It’s all about making the right trade-offs for your use case. But this book will highlight the different aspects and properties you should look for when picking a technique. We’ll start with simple scenarios in this chapter and work our way through more sophisticated ones after that.

2.1 Introducing The Tractor Store

Tractor Models, Inc., an imaginary startup, manufactures high-quality tin toy models of popular tractor brands. Currently, they are in the process of building an e-commerce website: The Tractor Store. It allows tractor fans from all over the world to purchase their favorite models.

To cater to their audience as best as possible, they want to experiment and test different features and business models. The concepts they plan to validate are offering deep customization options, auctions for premium material models, regionally limited special editions, and booking private in-person demos in flagship stores in all major cities.

To achieve maximum flexibility in development, the company decided to build the software from scratch and not go with an off-the-shelf solution. The company wants to evaluate their ideas and features quickly. That is why they decided to go with the micro frontends architecture. Multiple teams can work in parallel, independently build new features, and validate ideas. They are starting with two teams.

We’ll set up the software project for both Team Decide and Team Inspire. Team Decide will create a product detail page for all tractors that displays the name and image of the model. Team Inspire will provide matching recommendations. In the first iteration, each team displays its content on a separate page from its own domain. They connect the pages via links. So we have a product page and a recommendation page for every model.

2.1.1 Getting started

Now both teams start setting up their applications, deployment processes, and everything that is required to get their pages ready.

Freedom of choosing technology

Team Decide chooses to go with a MongoDB database for their product data and a Node.js application, which renders HTML on the server side. Team Inspire plans to use data science techniques. They’ll implement machine learning to deliver personalized product recommendations. That’s why they picked a Python-based stack.

Being able to choose the technology that’s best for the job is one of the benefits of micro frontends. It takes into account that not all tasks are the same. Building a high-traffic landing page has different requirements than developing an interactive tractor configurator.

Technology diversity and blueprints

Just because you can does not mean you must use different technology stacks for each team. When teams use similar stacks, it’s easier to exchange best practices, get help, or move developers between teams.

It can also save up-front costs because you could implement the basic application setup, including folder structure, error reporting, form handling, or the build process once. Every team can then copy this blueprint application and build on it. This way, teams can get productive a lot quicker, and the software stacks are more similar. In chapter 13, we’ll go deeper into this topic.

Independent deploys

Both teams create their own source code repository and set up a continuous integration pipeline. This pipeline runs every time a developer pushes new code to the central version control system. It builds the software, runs all kinds of automated tests to ensure the software’s correctness, and deploys the new version of the application to the team’s production server. These pipelines run independently. A software change in Team Decide will never cause Team Inspire’s pipeline to break. (See figure 2.1.)

Figure 2.1 Teams work in their own source code repository, have separate integration pipelines, and can deploy independently.

2.1.2 Running this book’s example code

For the integration techniques in the following chapters, the server-side technology stack is irrelevant. In our sample code, we’ll focus on the HTML output the applications generate. We’ll create a folder for every team which contains static HTML, JS, and CSS files, which we will serve through an ad hoc HTTP server.

Tip You can browse the source code of this book on GitHub 1 or download a ZIP from the Manning website 2. If you don’t want to run the code locally, you can go to https://the-tractor.store. There you can see and inspect all examples directly in your browser.

Directory structure

The examples all follow the same structure. Inside of each example folder like 01_pages _links, you’ll find a folder for each team like team-[name]. Figure 2.2 shows an example.

A team folder represents a team’s application. Code from one team folder never directly references code from another team’s folder.

Figure 2.2 The directory structure of the example code bundle. The main directory (shown as /) contains a sub-folder for each example project. The top-level package .json file contains the run commands for all examples.

Node.js required

Static assets like JS and CSS will go into the static folders later. You’ll need to have Node.js installed to run the ad hoc server. If you haven’t already, go to https://nodejs.org/ and follow the installation instructions. All examples run with Node.js v12. Higher versions should also work.

NOTE We are not assuming a specific terminal or shell throughout this book. The commands work in Windows PowerShell, Command Prompt, or Terminal on macOS and Linux.

Install dependencies

Navigate your terminal into the root directory for the sample code. There’s a package .json file that contains a start script for each example project. Install the required dependencies:

npm install

Starting an example

You can start each example from the root directory by running npm run [name_of _example]. Try this for our first example by typing this into your terminal:

npm run 01_pages_links

Each run command performs three actions:

  1. It starts a static web server for each team directory. It uses ports 3000 to 3003 for this.

  2. It opens the example page in your default browser.

  3. It shows an aggregated network log for all applications in the terminal.

NOTE Make sure ports 3000 to 3003 are not occupied by other services on your machine. If a port is blocked, the start script will not fail, but will start the application on another random port. Check the log if you’re experiencing issues.

Running the command for our first example should have started two servers on ports 3001 and 3002. Your browser should show the product page with a red tractor at http://localhost:3001/product/porsche.

Your terminal output should look like this:

$ npm run 01_pages_links
 
> [email protected] 01_pages_links [...]
> concurrently --names 'decide ,inspire' "mfserve --listen 3001 
01_pages_links/team-decide" "mfserve --listen 3002 01_pages_links/team-inspire"
"wait-on http://localhost:3001/product/porsche && opener  http://localhost:3001/product/porsche"
 
[decide ] INFO: Accepting connections at http://localhost:3001    
[inspire] INFO: Accepting connections at http://localhost:3002    
[2] wait-on http://localhost:3001/product/porsche && opener  http://localhost:3001/product/porsche exited with code 0"    
[decide ] :3001/product/porsche                                   
[decide ] :3001/static/page.css           )                       
[decide ] :3001/static/outlines.css                               

Started Team Decide’s server on port 3001 and Team Inspire’s server on port 3002

Opening the example page in your default browser

Shows the three network calls Team Decide’s application answered for the example page

NOTE The ad hoc web server uses the @microfrontends/serve package. It’s a modified version of the great zeit/serve server. I’ve added some features like logging, custom headers, and support for delaying requests. We’ll need these features in the following chapters.

You can stop the web server by pressing [CTRL] + [C].

With the setup and organizational stuff out of the way, we can start to focus on integration techniques.

2.2 Page transition via links

In the first iteration of their development, the teams choose to keep it as simple as possible. No fancy integration technique. Every team builds its feature as a standalone page. The team’s applications serve these pages directly. Each team brings its own HTML and CSS.

2.2.1 Data ownership

We start with three tractor models. In table 2.1 you see the data necessary for delivering a product page: a unique identifier (SKU), name, and image path.

Table 2.1 Team Decide’s product database

SKU

Name

Image

porsche

Porsche Diesel Master 419

https://mi-fr.org/img/porsche.svg

fendt

Fendt F20 Dieselroß

https://mi-fr.org/img/fendt.svg

eicher

Eicher Diesel 215/16

https://mi-fr.org/img/eicher.svg

Team Decide owns the base product data. They’ll build tools that enable employees to add new products or update existing ones. Team Decide is also responsible for hosting the product images. They upload the images to a CDN where other teams can directly reference them.

Team Inspire also needs some product data. They must know all existing SKUs and the associated image URL. That’s why Team Inspire’s backend regularly imports this data from Team Decide’s data feed. Team Inspire keeps a local copy of the relevant fields in their database. In the future, they’ll also consume analytics and purchase history data to improve their recommendation quality. But for now, the product recommendations will be hard-coded. Table 2.2 shows Team Inspire’s product relations.

Table 2.2 Team Inspire’s recommendations

SKU

Recommended SKUs

porsche

fendt, eicher

eicher

porsche, fendt

fendt

eicher, porsche

Team Decide doesn’t have to know anything about these relations. Nor do they need to know about the underlying algorithms and data sources.

Contract between the teams

In this integration, the URL is our contract between the teams. Teams that own a page publish their URL patterns. The others can use the patterns to create a link. Here are the patterns for both teams:

  • Team Decide: Product Page URL-pattern: http://localhost:3001/product/<sku> example: http://localhost:3001/product/porsche

  • Team Inspire: Recommendation Page URL-pattern: http://localhost:3002/recommendations/<sku> example: http://localhost:3002/recommendations/porsche

Because we’re running this locally, we use localhost instead of a real domain. We pick ports 3001 (Team Decide) and 3002 (Team Inspire) to differentiate the teams. In a live scenario, the teams could pick any domain they like.

When both applications are ready, the result should look like figure 2.3. The product page shows the name and image of the tractor, and links it to the corresponding recommendation page. The recommendation page shows a list of matching tractors. Each image links to the matching product page.

Figure 2.3 A product and recommendations page connected via links

 

Let’s take a quick look at the code that’s involved in making this happen.

2.2.3 How to do it

You can find the code for this example in the 01_links_pages folder. Figure 2.4 shows the directory listing.

Figure 2.4 A product and recommendations page connected via links

The HTML files represent the server-generated output of the teams. Each team also brings its own CSS file.

NOTE The ad hoc web server defaults to an .html extension when looking up a file. Requests to /product/porsche will serve the ./product/porsche.html file.

Markup

Take a quick look at the HTML of a product page. We’ll build on this markup throughout the examples of this book.

Listing 2.1 team-decide/product/porsche.html

<html>
  <head>
    <title>Porsche-Diesel Master 419</title>
    <link href="/static/page.css" rel="stylesheet" />
  </head>
  <body class="layout">
    <h1 class="header">The Tractor Store</h1>
    <div class="product">
      <h2>Porsche-Diesel Master 419</h2>
      <img class="image" src="https://mi-fr.org/img/porsche.svg" />
    </div>
    <aside class="recos">
      <a href="http://localhost:3002/recommendations/porsche">      
        Show Recommendations
      </a>
    </aside>
  </body>
</html>

The link to Team Inspire’s matching recommendation page

The markup for the other product pages looks similar. The important thing here is the Show Recommendations link. It’s our first micro frontends integration technique. Team Decide generates the link according to the URL pattern provided by Team Inspire.

Let’s switch to Team Inspire. The markup for a recommendation page looks like this.

Listing 2.2 team-inspire/recommendations/porsche.html

<html>
  <head>
    <title>Recommendations</title>
    <link href="/static/page.css" rel="stylesheet" />
  </head>
  <body class="layout">
    <h1 class="header">The Tractor Store</h1>
    <h2>Recommendations</h2>
    <div class="recommendations">
      <a href="http://localhost:3001/product/fendt">      
         <img src="https://mi-fr.org/img/fendt.svg" />    
       </a>                                               
       <a href="http://localhost:3001/product/eicher">    
         <img src="https://mi-fr.org/img/eicher.svg" />   
       </a>                                               
    </div>
  </body>
</html>

Links to Team Decide’s product pages

Again, the markup for the other tractors' pages is the same but shows different recommendations.

Styles

You may have noticed that both teams bring their CSS files. When you compare these files (team-decide/static/page.css vs. team-inspire/static/page.css), you’ll find redundancy. Both teams include basic layout, reset, and font styles.

We could introduce a master CSS file that all teams include. Having centralized styling might sound like a good idea. However, relying on a central CSS file introduces a considerable amount of coupling. Since micro frontends is all about decoupling and maintaining team autonomy, we have to be careful--even with styling.

In chapter 12, we’ll discuss the coupling aspect in greater detail and illustrate different solutions for shipping a coherent user interface across teams. So, for the examples in the following chapters, we’ll have to live with this styling redundancy.

Starting the applications

Let’s run the example and look at it in the browser. Execute the following command in the sample codes root folder:

npm run 01_pages_links

It opens http://localhost:3001/product/porsche in your browser, and you see the red Porsche Diesel Master tractor. The result should look like the screenshot in figure 2.5.

You can click on the “Show Recommendations” link to see other matching tractors on Team Inspire’s recommendation page. From there, you can jump back to a product page by clicking on another tractor. In the browser address bar, you see the browser jumping from localhost:3001 to localhost:3002.

Figure 2.5 Team Decide’s product detail page. The team owns everything on this page.

Congratulations, we’ve created our first e-commerce project that adheres to the micro frontends principles. The following sections will build on this code so that we can focus more on the actual integration techniques and care less about the boilerplate.

2.2.4 Dealing with changing URLs

The integration works because both teams exchanged their URL patterns beforehand. URLs are a popular and powerful concept which we will also see with other integration techniques. Sometimes URLs need to change because your application migrated to another server, a new scheme would be better for search engines, or you want language-specific URLs. You can manually notify all other teams. But when the number of teams and URLs grows, you’ll want to automate this process.

A team that wants to change its URL could provide an HTTP redirect to solve this. However, letting your end users jump through redirect chains is not always optimal. A more robust mechanism that has proven valuable for the projects we’ve worked on is that every team provides a machine-readable directory of all their URL patterns. A JSON file in a known location usually does the trick. This way, all applications can look up the URL patterns regularly and update their links if needed. Standards like URI templates, 3 json-home, 4 or Swagger OpenAPI 5 can help here.

2.2.5 The benefits

Though the outcome might not look impressive, the solution we just built has two properties that are important for running a micro frontends application. The coupling between the two applications is low, and the robustness is high.

Loose coupling

In this context, coupling describes how much one team needs to know about the other team’s system to make the integration work. In this example, every team only needs to implement the other team’s URL pattern to link to them. A team does not have to care about what programming language, frameworks, styling approach, deployment technique, or hosting solution the other team uses. As long as the sites are available at the previously defined URLs, everything works magically. We see the beauty of the open web in action here.

High robustness

When the recommendation application goes down, the detail page still works. The solution is robust because the applications share nothing. They bring everything they need to deliver their content. An error in one system cannot affect the other team’s system.

2.2.6 The drawbacks

The fact that the teams share nothing does come with a cost. An integration via links only is not always optimal from the user’s point of view. They have to click a link to see the information that is owned by another team. In our case, the user bounces between the product and the recommendation page. With this simple integration, we have no way of combining data from two different teams into one view.

This model also comes with a lot of technical redundancy and overhead. Common parts like the page header need to be created and maintained by each team.

2.2.7 When do links make sense?

When you are building a somewhat complicated site, an integration that relies on links only is not sufficient in most cases. Often you need to embed information from another team. But you don’t have to use links alone. They play well with other integration techniques.

2.3 Composition via iframe

The whole company staff is pleased about the progress both teams made in this short amount of time. But everyone agrees that we have to improve the user experience. Discovering new tractors via the Show Recommendations link works, but is not obvious enough for the customer. Our first studies show that more than half of the testers did not notice the link at all. They left the site under the assumption that The Tractor Store only offers one product.

Figure 2.6 Integrating the recommendation page into the product page via iframe. These pages don’t share anything. Both are standalone HTML documents with their own styling.

The plan is to integrate the recommendations into the product page itself. We’ll replace the Show Recommendations link on the right side. The visual style of the recommendations can stay the same.

In a short technical meeting, both teams weighed possible composition solutions against each other. They quickly realized that composition via iframe would be the fastest way to get this done.

With iframes, it’s possible to embed one page into another page while maintaining the same loose coupling and robustness properties that the link integration provides. Iframes come with strong isolation. What happens in the iframe stays in the iframe. But they also have significant drawbacks, which we’ll also discuss in this chapter.

Only a few lines of code have to be changed by each team. Figure 2.6 illustrates how the recommendations look on the product page. It also shows the team responsibilities. The complete recommendation page gets included on the product page.

2.3.1 How to do it

Ok, off to work. Our first task is to replace the Show Recommendations link. Team Decide can do that in their HTML, as the following code shows.

Listing 2.3 team-decide/product/porsche.html

...
<iframe src="http://localhost:3002/recommendations/porsche"></iframe>
...

After that, Team Inspire removes the “The Tractor Store” header from the recommendation page’s markup because we don’t need it in the iframe.

You find the updated example code in the 02_iframe folder. Run it via this command:

npm run 02_iframe

Your browser shows the recommendations inlined into the product page like you’ve seen before in figure 2.6.

There’s one other code change Team Decide had to do to make the iframe composition work. Iframes have one major drawback when it comes to layout. The outer document needs to know the exact height of the iframe’s content to avoid scrollbars or whitespace. Team Decide added this code to their CSS.

Listing 2.4 team-decide/static/page.css

...
.recos iframe {
  border: 0;         
  width: 100%;       
  height: 750px;     
}

Remove the browser’s default iframe border.

Iframe should be as wide as its parent container.

Fixed height to make enough space for the content

For static layouts, this might not be an issue, but if you’re building a responsive site, it can become tricky. The height of the content might change depending on the size of the device.

Another issue is that Team Inspire is now bound to the height Team Decide has defined. They can’t, for example, experiment by adding a third recommendation image without having to talk to the other team. JavaScript libraries 6 exist to automatically update the iframe size when its content changes.

The contract between the teams has become more complicated. Before, teams only needed to know the URL. Now they must also know the height of its content.

2.3.2 The benefits

In theory, the iframe is the optimal composition technique for micro frontends. Iframes work in every browser. They provide strong technical isolation. Scripts and styles can’t leak in or out. They also bring a lot of security features to shield the team’s frontends against each other.

2.3.3 The drawbacks

While iframes provide high isolation and are easy to implement, they also have a lot of negative properties, which has led to the iframe’s lousy reputation in web development.

Layout constraints

As already discussed, the absence of a reliable solution for automatic iframe height is one of the most significant drawbacks in day-to-day use.

Performance overhead

Heavy use of iframes is terrible for performance. Adding an iframe to a page is a costly operation from the browser’s perspective. Every iframe creates a new browsing context, which results in extra memory and CPU usage. If you are planning to include many iframes on a page, you should test the performance impact they introduce.

Bad for accessibility

Structuring the content of your page semantically is not only a hygienic factor. It enables assistive technologies like screen readers to analyze the page’s content and gives visually impaired users the ability to interact with the content via voice. Iframes break the semantics of the page. We can style an iframe to blend in with the rest of the page seamlessly. But tools like screen readers have a hard time conceptualizing what’s going on. They see multiple documents that all have their own title, information hierarchy, and navigation state. Be careful with iframes if you don’t want to break your accessibility support.

Bad for search engines

When it comes to search engine optimization (SEO), iframes also have a bad reputation. A crawler would index our product page as two distinct pages: the outer page and the included inner page. The search index does not represent the fact that one includes the other. Our page would not show up for the search term “tractor recommendations.” The user sees both words in their browser window, but these words do not exist in the same document.

2.3.4 When do iframes make sense?

These are quite strong arguments against the use of iframes. So when does an iframe make sense at all? As always, it depends on your use case.

Spotify, for example, implemented a micro frontends architecture early on for their desktop application. 7 Their integration technique relied on using iframes for the different parts of the application. Since the overall layout of their application is quite static, and search engine indexing is not an issue, this was an acceptable trade-off for them.

You shouldn’t use iframes if you are building a customer-facing site where loading performance, accessibility, and SEO matter. But for internal tools, they can be an excellent and straightforward option to get started with a micro frontends architecture.

2.4 What’s next?

In this chapter, we’ve successfully built a micro frontends application. Two teams can develop and deploy their part of the application autonomously. Both applications are decoupled. When one application breaks, the other still works.

Take a look at the integration techniques in figure 2.7. You’ve already seen these three types of integration in the big-picture diagram in chapter 1.

Figure 2.7 We can divide frontend integration techniques into three categories: routing, composition, and communication.

We covered the first two groups, transitioning between different teams pages using a link and using the iframe as a composition technique to include content from another team. We didn’t need communication yet.

In the next chapters, we’ll fill our toolbox with more server- and client-side integration techniques. We’ve arranged the chapters by complexity--starting with the simplest and working our way up to more sophisticated methods.

In chapter 9 we’ll zoom out a bit. We discuss different micro frontend high-level architectures like building server rendered pages or constructing a single page app composed out of other single-page apps (unified SPA).

If you have a clear project in mind and you’re limited in time, there’s a shortcut. Feel free to skip to chapter 9 to get an overview and decide which architecture fits best. You can then selectively jump back to the chapters that discuss its required techniques.

Summary

  • Teams should be able to develop, test, and deploy independently. That’s why it’s crucial to avoid coupling between their applications.

  • Integration via links or iframes is simple. A team only needs to know the URL patterns of the other teams.

  • Each team can build, test, and deploy their pages with the technology they like.

  • High isolation and robustness--when one system is slow or broken, the other systems are not affected.

  • A page can be integrated into other pages via iframes.

  • A page which integrates another page via iframe needs to know the size of its content. This knowledge introduces new coupling.

  • Iframes provide strong isolation between the teams. No shared code conventions or namespacing for CSS or JavaScript are required.

  • Iframes are suboptimal for performance, accessibility, and search engine compatibility.


1.Sample code on GitHub: http://mng.bz/QyOQ.

2.Micro Frontends in Action, http://mng.bz/XPjp.

3.See https://tools.ietf.org/html/rfc6570.

4.See “Home Documents for HTTP APIs,” https://mnot.github.io/I-D/json-home/.

5.See the Swagger OpenAPI Specification, https://swagger.io/specification/.

6.See iframe-resizer, https://github.com/davidjbradshaw/iframe-resizer.

7.See Mattias Peter Johansson, “How is JavaScript used within the Spotify desktop application?” Quora, http://mng.bz/Mdmm.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.12.108.236