Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Save more on your purchases! discount-offer-chevron-icon
Savings automatically calculated. No voucher code required.
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Newsletter Hub
Free Learning
Arrow right icon
timer SALE ENDS IN
0 Days
:
00 Hours
:
00 Minutes
:
00 Seconds
Arrow up icon
GO TO TOP
The Art of Micro Frontends

You're reading from   The Art of Micro Frontends Build highly scalable, distributed web applications with multiple teams

Arrow left icon
Product type Paperback
Published in Oct 2024
Publisher Packt
ISBN-13 9781835460351
Length 356 pages
Edition 2nd Edition
Arrow right icon
Author (1):
Arrow left icon
Florian Rappl Florian Rappl
Author Profile Icon Florian Rappl
Florian Rappl
Arrow right icon
View More author details
Toc

Table of Contents (27) Chapters Close

Preface 1. Part 1:The Hive – Introducing Frontend Modularization FREE CHAPTER
2. Chapter 1: Why Micro Frontends? 3. Chapter 2: Common Challenges and Pitfalls 4. Chapter 3: Deployment Scenarios 5. Chapter 4: Domain Decomposition 6. Part 2: Dry Honey – Implementing Micro Frontend Architectures
7. Chapter 5: Types of Micro Frontend Architectures 8. Chapter 6: The Web Approach 9. Chapter 7: Server-Side Composition 10. Chapter 8: Edge-Side Composition 11. Chapter 9: Client-Side Composition 12. Chapter 10: SPA Composition 13. Chapter 11: Siteless UIs 14. Part 3: Bee Brood – Implementation Details
15. Chapter 12: Sharing Dependencies with Module Federation 16. Chapter 13: Isolating CSS 17. Chapter 14: Securing the Application 18. Chapter 15: Decoupling Using a Discovery Service 19. Part 4: Busy Bees – Scaling Organizations
20. Chapter 16: Preparing Teams and Stakeholders 21. Chapter 17: Dependency Management, Governance, and Security 22. Chapter 18: Impact of Micro Frontends on UX and Screen Design 23. Chapter 19: Building a Great Developer Experience 24. Chapter 20: Case Studies 25. Index 26. Other Books You May Enjoy

Performance

Performance is almost always regarded as a feature. While different kinds of applications may be satisfied with different levels of performance, especially for end-user-facing applications, the general mantra is faster is better. Amazon claims that every 100 ms of additional latency costs them about 1% in sales. This is a massive number and should not be underestimated.

Resource caching

As we saw in Chapter 1, there are multiple kinds of websites these days, such as the following:

  • Static websites
  • Dynamic websites (server-side rendering or SSR)
  • Dynamic websites with AJAX
  • Single-page applications (SPAs)

Every kind has its own techniques to boost performance and stay scalable. For instance, static websites will use an in-memory cache to avoid reading the page from the disk on every request. This simple feature can be already used in SPAs; however, it must be implemented by hand for SSR.

Most languages and frameworks that deal with SSR have no idea what kinds of dependencies are required to create a response for a given request. As such, introducing an automatic cache for these requests seems very difficult. We can assist these frameworks by giving hints.

There are multiple layers of caching that we can apply to help not only our server but also the browser to improve performance with a cache. Ideally, pretty much all frontend assets are uniquely named and can be cached indefinitely in the client.

Important note

There are several HTTP headers dedicated to properly communicating caching. While we focus on the strongest caching guarantees here, in general, more fine-grained approaches can be very useful, too. A full discussion on caching would certainly go beyond the scope of this book. See the following MDN link: https://developer.mozilla.org/en-US/docs/Web/HTTP/Caching.

At some point, you may be tempted to aggregate multiple assets into a single asset. For instance, if three micro frontends all produce a JavaScript file, you may want to bring this to a single JavaScript file. While this can be beneficial, the complexity and its outcome might not justify this investment. There are multiple reasons for this:

  • HTTP2 streams requests and makes serving multiple resources almost as efficient as serving a single resource
  • Caching becomes more complex and is less fine-grained, leading to more cache misses
  • The individual scripts lose their context – for example, to automatically resolve URLs correctly
  • One erroneous script can abort the execution of all scripts in the aggregation

Consequently, even consuming resources per micro frontend directly in the browser is usually better than a single large resource containing all micro frontends.

Bundle size

For SPAs or JavaScript-heavy pages in general, we need to be very cautious with the used JavaScript code. In today’s landscape, it’s far too easy to use multiple dependencies carelessly. Monitoring the resulting size of the JavaScript bundles and making sure that these bundles remain small is vital to great performance.

Especially for SPA-based micro frontends, we will use a bundler such as webpack or Vite to produce one or more JavaScript files for each micro frontend. Here, it is crucial to leverage bundle splitting to keep each JavaScript file rather small. A classic example would be to create a separate JavaScript file for each page in a SPA.

So, instead of writing code such as the following, you leverage the import function to introduce bundle splitting by lazy loading another JavaScript fragment:

import * as React from 'react';
import { Switch, Route } from 'react-router-dom';
import MyPage from './page';
export const Routes = (
  <Switch>
    <Route path="/my-page" component={MyPage} />
  </Switch>
);

In React, we can use the lazy function together with a Suspense component to allow lazy loading for parts of the application.

Updating the example to use lazy loading, we get the following:

import * as React from 'react';
import { Switch, Route } from 'react-router-dom';
const MyPage = React.lazy(() => import('./page'));
export const Routes = (
  <React.Suspense fallback={<div>Loading...</div>}>
    <Switch>
      <Route path="/my-page" component={MyPage} />
    </Switch>
  </React.Suspense>
);

For micro frontends, bundle splitting may be even more crucial than for non-micro frontend web applications. Since scalability is one of the core principles of most web applications, the only way to ensure a fluent user experience is to create smaller assets that are only loaded on demand.

Important note

It always makes sense to be aware of what dependencies contribute to the final bundle size. For webpack, the webpack-bundle-analyzer package visualizes this quite nicely. Also, websites such as https://bundlephobia.com help to identify the cost of including dependencies quite early.

In general, we should try to introduce the lazy loading of individual code as much as possible. As seen in the previous example, in SPAs, this can be as easy as lazy loading every component representing a page. Also, dependencies that are only used in “exotic” scenarios should not be part of the main bundles. Using modern Promise infrastructures helps a lot here.

Request optimizations

Micro frontends are all about modularization. As a result, the number of HTTP requests will definitely grow. After all, instead of having everything integrated into a monolith, we now have a system where the different technical parts are still very much separated – either on a server or in the browser.

Quite often, many modules will need to request the same or similar data. If we are not careful, this may lead to a lot of stress on the API servers. Even worse, aggregating the results will take longer, leading to a worse user experience. It can, therefore, be useful to mitigate this by introducing micro-caching for API requests. If a request to /api/user is cached for 1 s, then other execution units get the result immediately from the cache. Importantly, on the server side, this cache has to be exclusive to the page’s request context.

Another factor to think about is request batching. Especially with a microservice backend, there may be many requests needed to get some required information aggregated. If we introduce a technology such as GraphQL on the API gateway, we could run a single request with multiple queries. The backend then resolves all the different queries – using a much faster and more direct way than the client could. Here, the backend may cache the queries, too.

Now that we’ve touched upon request optimization, with its potential security challenges, it’s time to look a little into the security sector in more detail.

lock icon The rest of the chapter is locked
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $19.99/month. Cancel anytime
Banner background image