Preface – Building Micro-Frontends

Preface

At the beginning of December 2016, I was taking my first trip to Tokyo.
It should have lasted just a week, but as I would discover, I would need to travel to the Japanese capital many more times in the following weeks.

I clearly remember walking to the airplane and mentally preparing my to-do list for the impending 12-hour flight. By now I’d been travelling for a couple of weeks, on the opposite side of the world: attending a couple of conferences in the Bay area and then another event in Las Vegas.

The project I was working at that time was an over-the-top platform similar to Netflix, but dedicated to sports, with daily live and on-demand content available in multiple countries and on more than 30 different devices (web, mobile, consoles, set-top-boxes and smartTVs).

It was near the end of the year, and as a Software Architect I had to make a proposal for a new architecture that would allow the company to scale to hundreds of developers distributed in different locations, without reducing the current throughput and enhancing it as much as I could.

When I was setted in my seat, I became relatively calm. I was still tired from the Vegas trip, and a bit annoyed at the 12 hours I would have to spend on the airplane, but excited to see a mystical country like Japan for the first time.
A few minutes later I had my first glass of Champagne. For the first time in my life, I was in business class, with a very comfortable seat and a lots of space for working.
Definitely the best place to start such a long journey.

At the end of the first hour, it was time to get my laptop out of my backpack and start working on “the big plan”, I still had more than 10 hours of flight time during which I could start working on this huge project that would serve millions of customers around the world. I didn’t know at that time that the following hours would deeply change the way I would architect cross-platform applications for frontend.

In this book I want to share my journey into the Micro-Frontends world, all the lessons and tips for getting a solid Micro-Frontends architecture up and running, finally the PROs and CONs of this approach.
These lessons will allow you to evaluate if this architecture would fit your current or next project.
So now it’s time for your journey to begin.

The Frontend Landscape

I remember a time when web applications were called RIAs or Rich Internet Applications, to differentiate them from the traditional, and more static, corporate websites. Nowadays we can find many “RIAs”, or simply web applications, across the World Wide Web.

Now, many SaaS (Software as a Service) with more or less complex UIs allow us to print our business cards on demand, watch our favorite movies or live events, order a few pepperoni pizzas for us and our guests, manage our bank accounts from our comfortable sofas, and do many, many other things that make our life easier on a daily basis.

As CTOs, architects, tech leads, or simply developers, when we start a greenfield project, we can create a single-page application or an isomorphic one (also called a universal application), or instead work with a bunch of static pages that run in our cloud or on-premise infrastructure.

Yet despite this broad range of opportunities in front of us, not all of them are fitting for any project.
Instead, we need to understand first what challenges we are going to face before taking a direction.

But before jumping straight to the topic of this book, let’s analyze the current architectures available to us when we work on a frontend application.

Single-Page Applications

Single-page applications (or SPAs) are probably the most used implementations by many companies. They consist of a single or a small number of JavaScript files that encapsulate the entire frontend application usually downloaded up front.

When the web servers or the Content Delivery Network (CDN) serve the HTML index page, the SPA loads the JavaScript, CSS, and any additional files needed for displaying any part of our application.

There are many benefits of using SPAs; for instance, the client downloads the application code once at the beginning of its life cycle and the entire logic is available up front.
Also SPAs usually communicate with APIs for exchanging data with the persistent layer of the backend.

An SPA avoids multiple round trips to the server for loading additional application logic and renders all the views instantaneously during the application life cycle.
Both features enhance the user experience and simulate what we usually have when we interact with a native application for mobile devices or desktop, where we can jump from one part of our application to another without waiting too long.

In addition, an SPA fully manages the routing mechanism on the client side.
Usually every time the application changes a view, it rewrites the URL in a meaningful way to allow users to share the page link or bookmark the URL for starting the navigation from a specific page. SPAs also allow us to decide how we are going to split the application logic between server and client. We can have a “fat client” and a “thin server” where the logic is mainly stored on the client side and the server side is used as persistence layer, or a “thin client” and a “fat server” where the logic is mainly delegated to the backend, and the client doesn’t perform any smart logic but just reacts to the state provided by the APIs.

Over the past of several decades, different school of thoughts have prevailed on whatever far or thin clients are better solutions.
Despite these arguments altough, both approaches have their own PROs and CONs. It always depends on the type of application we are creating.
I personally found it very valuable to have a thin client and a fat server when I was targeting cross-platform applications, so I was able to design a feature once and have all clients deployed on multiple targets react to the application state stored on the server.
At the same time, I often used a fat client and a thin server when I had to create desktop applications where the offline persistence layer was an essential feature; therefore, rather than writing the state logic twice, I managed it only in one place and used the server for data synchronization.

However, SPAs have some disadvantages for a certain type of applications. Technically speaking, the first time to load usually takes longer than for other architectures, because we are downloading the entire application instead of only what the user needs to see
If not well designed, this could become a killer for our applications, especially, when they are loaded with an unstable or unreliable connection on mobile devices like smartphones and tablets.

Nowadays there are several ways to mitigate this problem caching the content directly on the client. In particular Progressive Web Apps are providing a set of new techniques, based on service workers, a script that your browser runs in the background, separate from a web page, for enhancing the user experience of an SPA serves on mobile devices with flaky or totally absent connections. Another potential issue caused by SPAs is that are not SEO-friendly; in fact, when a crawler is trying to understand how to navigate the application or website, it won’t have an easy job indexing all the contents served by an SPA unless we prepare alternative ways for fetching it.

Usually when we want to provide better indexing for an SPA, we tend to create a custom experience strictly for the crawler.
For instance, Netflix lowers its geo-fencing mechanism when the user-agent requesting their web application is identified as a crawler, instead serving content similar to what a user would watch based on the country specified in the URL. This is a very handy mechanism considering that the crawlers engine is often based in a single location from which it indexes a website all over the world.
As mentioned earlier, SPAs download all the application logic in one go, but this also can lead to potential memory leaks when the user is jumping from one view to another if the code is not well implemented and is not correctly disposing of the unused objects.

In large applications this could be a real problem that might lead to several days or weeks of application hardening in order to make the SPA code functional.
It could be even worse if the device that loads the SPA doesn’t have a great hardware, like a smart TV or a set-top-box.
Too often I have seen applications running smoothly on a MacBook Pro quad-core and then failing miserably when run on a low end device.

The last disadvantage to mention when we work with SPAs is on the organizational side.
When an SPA is a large application managed by distributed or colocated teams working on the same codebase, different areas of the same application could end up with a mix of approaches and decisions that could confuse team members.
Also the communication overhead teams will use to coordinate between themselves is often a hidden cost of the application.

Many times we completely forget about calculating the inefficiency of our teams, not because they are not capable of developing an application, but because the company structure or architecture doesn’t enable them to express in the best way possible, slowing down the operations and the implementation of any new feature.

Isomorphic Applications

Isomorphic applications, or universal applications, are web applications where the code between server and client is shared and can run in both contexts.

This technique brings some benefits when used in the right way. It is in particular is convenient when the time to interaction, A/B testing, and SEO are essential characteristics for the application.

Isomorphic applications can be designed in different ways.
Because these web applications share code between server and client, the server, for instance, can do the rendering part for the page requested by the browser, retrieve the data to display from the database or from one or multiple APIs, aggregate it together, and then pre-render it with the template system used for generating the view, in order to serve to the client a page that doesn’t need additional round trips for requesting data to display.

This will definitely enhance the time to interaction, considering the page requested is pre-rendered on the server and is partially or fully interpreted on the backend. This spares a lot of roundtrips on the frontend, so we won’t need to load additional resources (vendors, application code, etc.) and the browser will interpret a static page with almost everything inside.

Also, as mentioned, using isomorphic applications can improve an SEO strategy, because the page is server-side rendered without the need for additional server requests when served.
The crawler is going to request the page and receives it fully prepared on the server, ready to be indexed by the search engine without any problem. Isomorphic applications share the code between contexts, but how much code is really shared? To answer this question depends on the context.

For instance, we can use this technique in a hybrid approach where we server-side render part of the page in order to benefit from the time to interact and then lazy loading additional JavaScript files for having the benefits of the isomorphic application as well as the single page application where the files loaded within the HTML page served allow to add sophisticated behaviours to a static web page, transforming this page in a SPA. We can also have a more purist approach where we always render the page and its state on the server providing only a page to display for the browser, really depends on the complexity of the project we are facing.

With this approach, we can decide how much code is shared on the backend based on the project’s requirements.
For instance, we can render just the views, inlining the CSS and the bare minimum JavaScript code to have an HTML skeleton that is loaded very quicly by the browser, or completely delegate the rendering and data integration onto the server, pheraps because we have more static pages than heavy interactivity on the client side. We can also have a mixed approach where we divide the application into multiple SPAs with the first view rendered on the server side, and then some additional JavaScript to download for managing the application behaviors, the models and also the routing inside the SPA.

Routing is another interesting part of an isomorphic application because we can decide to manage it on the server-side, only serving a static page every time the user is interacting with a link on the client.
Or we can have a mixed approach if we use the benefits of server side rendering only for the first view, then load an SPA, where in this case the server will do a macro routing that serves different SPAs, each SPA has its own routing system for navigating between views. Another benefit of this approach is that we are not limited only to template libraries, but virtual DOM implementations like React, Preact, or similar, also can benefit from this approach. Many other libraries and frameworks have started to offer the server-side rendering approach out of the box like Vue with Nuxt.js, Meteor, or Angular, for instance. Also, we don’t need to have a Node.js backend for working with isomorphic apps but we can use the technology we are more familiar with for serving our APIs like Go or PHP for instance.
Isomorphic applications therefore won’t have much of an impact on your existing backend technology stack.

The last thing to mention about isomorphic applications is that we can integrate A/B testing platforms nicely without much effort. In the past year or so, many A/B testing platforms had to catch up with the frontend technologies in not only supporting UI libraries like JQuery but also in embracing virtual DOM libraries like React or Vue. They also have to make their platforms ready for hybrid mobile applications as well as native ones.

The strategy these companies adopted is to manage the experiments on the server side where the developers have full control of the experiments to run on the clients. This is obviously a great advantage if you are working with an isomorphic application because you can pre-render on the server the specific experiment you want to serve to a specific user. Those solutions can also communicate with the clients via APIs in the case of native mobile applications and SPAs for choosing the right experiment.

Isomorphic applications could also suffer from scalability problems if a project is really successful and visited by millions of users. Considering we are generating the HTML page pre-rendered on the server, it means we need to create the right caching strategy in order to minimize the impact on the servers.
In this case, if the responses are highly cacheable, CDNs like Akamai, Fastly, or Cloudfront could definitely improve the scalability of our isomorphic applications by avoiding all the requests hitting origin servers.

Organisation wise, an isomorphic application suffers of similar problems we can find on an SPA where the code base is unique and maintained by one or multiple teams.

There are ways to mitigate the communication overhead if a team is working on a specific area of the application without any overlap with other teams. In this case we can use architecture like Backend for Frontends (BFF) for decoupling the API implementation and allow each team to maintain their own layer of APIs specific to a target.

Static-Page Websites

Before entering the main topic of this book, it is worth mentioning the static-page website, where every time the user clicks on a link we are loading a new static page.
Fairly “old school”, I know, but still in use with some twists.

Usually this approach is useful for quick websites that are not meant to be online for a long period of time, such as ones where freelancers can promote their skills or simply for advertising a specific product or service we want to highlight without using the corporate website.

In the last few years this type of websites has mutated into a single page that expands vertically instead of loading different pages. Another trend in this last case is to lazy-load the content of the website, waiting till the user scrolls to a specific position of the website and then loading the content.

The same technique is used with hyperlinks, where all the links are anchors inside the same page and the user is browsing quickly between bits of information available on the website. These kinds of projects are usually created by small teams or individual contributors; the investment for the company is fairly low on the technical side, and it’s a good playground for developers to experiment with new technologies and new practices or to consolidate existing ones.

Micro-Frontends

Micro-Frontends are an emerging architecture, clearly inspired by microservices architecture.
The main idea behind them is to break down “the monolith” into smaller parts, allowing an organisation to spread the amount of work with autonomous teams, colocated or distributed, without the need to slowing down their delivery throughput.

As we know, encapsulating an API inside a microservice is actually the easiest part.
When we realize there is way more to take care of, we will understand the complexity of the microservices architecture that not only adds high flexibility and good encapsulation between domains, but also an overall complexity around the observability, automation, and discoverability of a system.

Micro-Frontends probably are not suitable for all projects; nevertheless, they can alert us to a new way to structure our frontend applications, solving some of the key challenges we have encountered in the past not only from a technical perspective but also from an organizational one.

Too often I have seen great architectures on paper that didn’t translate well in the real world because the creator didn’t take into account the environment (company’s structure, culture, developers skills, timeline, etc.) where the project should have been built.
As Conway’s law, mentioned in many books, claims:

The Conway’s law could be mitigated with the “Inverse Conway Maneuver,” which recommends that teams and organizations be structured on our desired architecture and not vice versa.

I truly believe that mastering different architectures and investing time on understanding how many systems work allows us to mitigate the impact of Conway’s law, because it gives us enough tools in our belt to solve different challenges, not only technical ones but organizational too.

Conventions Used in This Book

The following typographical conventions are used in this book:

Italic

Indicates new terms, URLs, email addresses, filenames, and file extensions.

Constant width

Used for program listings, as well as within paragraphs to refer to program elements such as variable or function names, databases, data types, environment variables, statements, and keywords.

Constant width bold

Shows commands or other text that should be typed literally by the user.

Constant width italic

Shows text that should be replaced with user-supplied values or by values determined by context.

Tip

This element signifies a tip or suggestion.

Note

This element signifies a general note.

Warning

This element indicates a warning or caution.

O’Reilly Online Learning

Note

For more than 40 years, O’Reilly Media has provided technology and business training, knowledge, and insight to help companies succeed.

Our unique network of experts and innovators share their knowledge and expertise through books, articles, and our online learning platform. O’Reilly’s online learning platform gives you on-demand access to live training courses, in-depth learning paths, interactive coding environments, and a vast collection of text and video from O’Reilly and 200+ other publishers. For more information, visit http://oreilly.com.

How to Contact Us

Please address comments and questions concerning this book to the publisher:

  • O’Reilly Media, Inc.
  • 1005 Gravenstein Highway North
  • Sebastopol, CA 95472
  • 800-998-9938 (in the United States or Canada)
  • 707-829-0515 (international or local)
  • 707-829-0104 (fax)

Email to comment or ask technical questions about this book.

For news and information about our books and courses, visit http://oreilly.com.

Find us on Facebook: http://facebook.com/oreilly

Follow us on Twitter: http://twitter.com/oreillymedia

Watch us on YouTube: http://www.youtube.com/oreillymedia

Acknowledgments

First of all, I’d like to thank my family, my girlfriend Maela, and my daughter Emma for everything we share and the strength I receive from them to move forward every single day. Thanks to all the people who inspire me on a daily basis in any shape or form of communication.

A huge thank to DAZN, who allowed me to apply a Micro-Frontends architecture and to explore the benefits of it end to end trusting my ideas and judgement.
I also thank all my incredible colleagues who challenged and helped me on delivering our platform on more than 30 different targets. Last but not least, thanks to O’Reilly for the opportunity to write about Micro-Frontends, in particular to Jennifer Pollock and Angela Rufino for all the support I had during this journey.