Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Exploring micro frontends vs monolithic architecture web performance #2089

anicioalexandre started this conversation in Show and tell
Discussion options

Hi guys, I've been doing some experiments with micro frontends architecture (MFA) built with module federation and trying to compare with the monolithic architecture (MA).

So I built two prototypes, one using MFA and another using MA. Both prototypes lives inside a monorepo so they can share same configs (lint, axios, tailwind, etc) and also a small design system with a few components. The idea was to make them equal as possible, so they only differ in architecture.

The MFA app was divided in three very simple apps (mf_app_shell, mf_app_album_search and mf_app_album_album) and then tried to analyze and run tests to compare both applications.

In general the monolithic app downloaded less bytes than the MFA one, also took less time to load in most of the metrics tested (I can send to you guys if needed, I did the tests on WebPageTest). Looking at the the scripts downloaded in one of the tests, the MA app downloaded ~80kb and ~114kb for the MFA app.

micro frontend app monolithic app
shell-album mono-album

There is a good chance that I messed up with the prototypes (meaning there can be some factors, apart from the architecture, that may influence on the results), also some miss configuration on the module federation plugin... Maybe dependency duplication? Or Bad dependency sharing?

So I would like to hear from you guys about these results and any thoughts on how I could improve the MFA prototype in general!

I also take the opportunity to thanks @ScriptedAlchemy and all the Webpack guys that made this possible :)

You must be logged in to vote

Replies: 1 comment

Comment options

Oversharing modules can lead to perf degradation since a shared module cannot be tree shaken since its "used in unknown ways"

Also MF apps tend to have more chunks since shared/exposed modules are chunked out so its selectively loaded.

A good trick is to check if its cheaper not to share a vendor module (since tree shaking)

Another one is to make custom cacheGroups under splitChunks.
So i can test a regex for anything containing "react" and force it back into a single js file, so less requests needed for things that generally will always be needed. To make it work, you'd need to put enforce:true on the cacheGroup to override webpack default chunking of MFP.

Remember, everything is a tradeoff. A single monolithic build will have slightly better optimization since webpack can analyze everything - but at the cost of organizational agility, deploy couplings, and far less flexibility.

Old School MFES (like externals, or multi app mount points) have terrible performance but are flexible and provide deploy independence.

Module Federation is a combination of the two - more optimized than multi apps or externals, less optimized than single build. But usually not by much.

I mostly work with SSR based apps - so ive not really seen any perf impacts compared to a single build, this is because i can track and flush all script tags needed upfront so i dont have to wait for webpack to kick in, then load more js, creating a bit of a waterfall.

On the flip side - looking at your load times - the monolith took over a second, whereas the federated app took 800ms.

That said, look at the number of network requests in comparison. One single file to load, vs several.

A more fair tests would be to have your monolith use react.lazy and dynamic imports throughout.

If you use import from for your federated modules, this also slows things down as webpack has to load everything you imported BEFORE the app can even start.

So i always have a few import() in place or react lazy, so im only loading what's required upfront and if something is not rendered, im not delaying app start in order to download a bunch of sync imported modules that im not actually using.

Also consider the real world situation... 30kb or so more is not a bad price to pay for the gains, especially if you lazy load the extra things os needed.

To compare, ive got at least 2mb of third party code which adds 300 additional network requests to one of my applications.
So at scale, as long as you keep an eye on bundle size and check if sharing a module is worth the size bump or not - you should be good. So definitely keep an eye on perf and build perf - and id be happy to chat over twitter as i do quite a lot in the web perf space to mitigate the massive third party bloat i cannot control. See if we can optimize it a little more.

But always keep a holistic view...
A tag manager like adobe launch or GTM can easily add about 2-3 seconds to your site load time. Comparatively, theres much worst perf hits with higher rewards than my app loading an extra 100kb - tho thats no excuse not to care by any means.

In this case, the build size increase aside (since thats likely due to sharing modules that then need to opt out of tree shaking) - it might be the network requests that and using too much import from now forcing a waterfall just to start the app

You must be logged in to vote
0 replies
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet

AltStyle によって変換されたページ (->オリジナル) /