{"id":4712,"date":"2021-06-01T22:13:52","date_gmt":"2021-06-01T20:13:52","guid":{"rendered":"https:\/\/www.angulararchitects.io\/?p=4712"},"modified":"2021-06-01T22:13:52","modified_gmt":"2021-06-01T20:13:52","slug":"multi-framework-and-version-micro-frontends-with-module-federation-the-good-the-bad-the-ugly","status":"publish","type":"post","link":"https:\/\/www.angulararchitects.io\/en\/blog\/multi-framework-and-version-micro-frontends-with-module-federation-the-good-the-bad-the-ugly\/","title":{"rendered":"Multi-Framework and -Version Micro Frontends with Module Federation: The Good, the Bad, the Ugly"},"content":{"rendered":"<blockquote><p>\nUpdated on 2021-12-23 for CLI 13.1.x and above\n<\/p><\/blockquote>\n<p>Recently, I've been asked several times how to combine Micro Frontends built with different frameworks and\/or framework versions. A quite modern and flexible approach for this is providing Web Components via Module Federation. <\/p>\n<p>Nevertheless, in general, combining different frameworks and versions is nothing the individual frameworks have been built for. Hence, there are some pitfalls, this articles shows some workarounds for.<\/p>\n<p>For this, here, I'm using an Angular-based shell that loads several Micro Frontends. They've been developed with two different Angular versions and React: <\/p>\n<p><img decoding=\"async\" src=\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2020\/12\/animated.gif\" alt=\"Micro Frontends using different frameworks and versions\" \/><\/p>\n<p>The shell and <em>Micro Frontend 1<\/em> use the same Angular version. Hence, they shall share it. The same is true for <em>Micro Frontend 2<\/em> and <em>Micro Frontend 3<\/em>. <em>Micro Frontend 4<\/em>, however, uses React:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2020\/12\/venn.png\" alt=\"Different frameworks and versions can be shared\" \/><\/p>\n<p>The <a href=\"https:\/\/github.com\/manfredsteyer\/multi-framework-micro-frontend\n\">source code<\/a> for this case study can be found <a href=\"https:\/\/github.com\/manfredsteyer\/multi-framework-micro-frontend\">here<\/a>.<\/p>\n<h2>The 1st Rule for Multi Framework(version) MFEs<\/h2>\n<p>Let's start with the 1st rule for multi framework and multi version micro frontend architectures: Don't do it ;-).<\/p>\n<p>Seriously, while wrapping applications into web components and loading them with Module Federation is not that difficult, there are several pitfalls along the way. Hence, I want to start with two alternatives:<\/p>\n<h3>Alternative 1: Evergreen Version with Module Federation<\/h3>\n<p>The Angular team is heavily investing in seamless upgrades. The Angular CLI command <code>ng update<\/code> is providing the results of this by the push of a button. It executes several migration scripts lifting your source code to the newest version. This and respective processes at Google allow them to always use the newest Angular version for all of their more than 2600 Angular applications.<\/p>\n<p>Also, if all parts of your system use the same major version, using Module Federation for integrating them is straightforward. We <strong>don't need<\/strong> Web Components to bridge the gap between different versions and from our framework's perspective, everything we do is using lazy loading. Underneath the covers, Module Federation takes care of loading separately compiled code at runtime. <\/p>\n<p>An example demonstrating this can be found in <a href=\"https:\/\/www.angulararchitects.io\/aktuelles\/the-microfrontend-revolution-part-2-module-federation-with-angular\/\">this article<\/a> that is part of the article series at hand. <\/p>\n<h3>Alternative 2: Relaxing Version Requirements + Heavy Testing<\/h3>\n<p>Another approach is to relax the requirements for needed versions. For instance, we could tell Module Federation that an application built with Angular 10 also works with Angular 11. For this, Module Federation provides the configuration property <code>requiredVersion<\/code> described <a href=\"https:\/\/www.angulararchitects.io\/aktuelles\/getting-out-of-version-mismatch-hell-with-module-federation\/\">here<\/a>. <\/p>\n<p>This might work because, normally, Angular's core didn't recently change much between major versions. However, this is not officially supported and hence you need a huge amount of E2E tests to make sure everything works seamlessly together. On the other side, you need E2E tests anyway as micro frontends are runtime dependencies not known upfront at compilation time.<\/p>\n<h2>A Good Message Before We Start<\/h2>\n<p>Most of the tricks and tweks I'm presenting here are meanwhile automated by my library <a href=\"https:\/\/www.npmjs.com\/package\/@angular-architects\/module-federation-tools\">@angular-architects\/module-federation-tools<\/a> which is an add-on for @angular-architects\/module-federation. Here you even find a <a href=\"https:\/\/red-ocean-0fe4c4610.azurestaticapps.net\/\">live demo<\/a>.<\/p>\n<p>Nethertheless, this article can be vital for you because it helps you to understand both, the underlying ideas and also how to use them -- automated via a library or hand-written -- in an Angular application.<\/p>\n<h2>The Good<\/h2>\n<p>Okay, let's go on with finding out how the combination of Module Federation and Web Components allows building multi framework(version) Micro Frontends. Before we look into the pitfalls, I'm going to tell you about the good aspects of this.<\/p>\n<h3>Sharing Libraries<\/h3>\n<p>As mentioned before, our case study is sharing two versions of Angular:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2020\/12\/venn.png\" alt=\"Different frameworks and versions can be shared\" \/><\/p>\n<p>For this, the shell and each Micro Frontend just needs to mention the libraries to share in their Module Federation config:<\/p>\n<pre><code class=\"language-javascript\">new ModuleFederationPlugin({\n  [...],          \n  shared: [&quot;@angular\/core&quot;, &quot;@angular\/common&quot;, &quot;@angular\/router&quot;]\n})<\/code><\/pre>\n<p>By default, Module Federation is using semantic versioning to find out the highest compatible version available. Let's say, we have the following constellation:<\/p>\n<ul>\n<li>Shell: @angular\/core@^<strong>12.0<\/strong>.0<\/li>\n<li>MFE1: @angular\/core@^<strong>12.1<\/strong>.0<\/li>\n<li>MFE2: @angular\/core@^<strong>13.1<\/strong>.0<\/li>\n<li>MFE3: @angular\/core@^<strong>13.0<\/strong>.0<\/li>\n<\/ul>\n<p>In this case, Module Federation decides to go with the following versions:<\/p>\n<ul>\n<li>Shell and MFE1: @angular\/core@^<strong>12.1<\/strong>.0<\/li>\n<li>MFE2 and MFE3: @angular\/core@^<strong>13.1<\/strong>.0<\/li>\n<\/ul>\n<p>In both cases, the respective highest compatible version is used. <a href=\"https:\/\/www.angulararchitects.io\/aktuelles\/getting-out-of-version-mismatch-hell-with-module-federation\/\">More details about this clever mechanism and configuration options to influence this behavior<\/a> can be found <a href=\"https:\/\/www.angulararchitects.io\/aktuelles\/getting-out-of-version-mismatch-hell-with-module-federation\/\">here<\/a>. <\/p>\n<h3>Exporting Web Components<\/h3>\n<p>Using Module Federation, a Micro Frontend (officially called <em>remote<\/em>) can expose all possible code fragments. In cases where all parts of the system use the same framework version, this could be an Angular modules or a component.<\/p>\n<p>If we have different framework versions, we can expose web components:<\/p>\n<pre><code class=\"language-javascript\">new ModuleFederationPlugin({\n  [...],\n  exposes: {\n    &#039;.\/web-components&#039;: &#039;.\/src\/bootstrap.ts&#039;,\n  },\n  [...]\n})<\/code><\/pre>\n<p>The <code>bootstrap.ts<\/code> file is bootstrapping an Angular application providing an Angular component as a Web Component with Angular Elements. To add Angular Elements to your project, use the following CLI command:<\/p>\n<pre><code class=\"language-javascript\">ng add @angular\/elements<\/code><\/pre>\n<p>The file <code>bootstrap.ts<\/code> uses the same source code the CLI normally generates for your <code>main.ts<\/code>. This includes calling the platform's <code>bootstrapModule<\/code> method with our <code>AppModule<\/code>.<\/p>\n<p>The <code>main.ts<\/code> file only contains the following dynamic <code>import<\/code>:<\/p>\n<pre><code class=\"language-typescript\">import(&#039;.\/bootstrap&#039;);<\/code><\/pre>\n<p>As mentioned in one of the previous articles of this series, this is a typical pattern for Module Federation. It gives the application the time necessary for negotiating the library versions to use and for loading them.<\/p>\n<p>In order to provide a web component, the Micro Frontend's <code>AppModule<\/code> uses Angular Elements:<\/p>\n<pre><code class=\"language-typescript\">[...]\nimport { createCustomElement } from &#039;@angular\/elements&#039;;\n[...]\n\n@NgModule({\n  imports: [\n    BrowserModule,\n    RouterModule.forRoot([...])\n  ],\n  declarations: [\n    [...]\n    AppComponent\n  ],\n  providers: [],\n  bootstrap: []\n})\nexport class AppModule {\n  constructor(private injector: Injector) {\n  }\n\n  ngDoBootstrap() {\n    const ce = createCustomElement(AppComponent, {injector: this.injector});\n    customElements.define(&#039;mfe1-element&#039;, ce);\n  }\n\n}<\/code><\/pre>\n<p>Please note that this <code>AppModule<\/code> doesn't have a bootstrap component. The <code>bootstrap<\/code> array is empty. Hence, Angular calls the module's <code>ngDoBootstrap<\/code> method. Here, <code>createCustomElement<\/code> wraps an Angular component as a web component (a custom element to be more precise). <\/p>\n<p>Also, it registers this Web Component with the browser using <code>customElements.define<\/code>. This method assigns the tag name <code>mfe1-element<\/code> to it. <\/p>\n<h3>Loading Micro Frontends<\/h3>\n<p>Also, loading a separately compiled micro frontend on demand is straightforward with Module Federation. For this, the shell (officially called <em>host<\/em>) can leverage the helper method <code>loadRemoteModule<\/code> provided by the package <a href=\"mailto:code&gt;@angular-architects\/module-federation&lt;\/code\">code>@angular-architects\/module-federation<\/code<\/a>.<\/p>\n<pre><code class=\"language-typescript\">import { loadRemoteModule } from &#039;@angular-architects\/module-federation&#039;;\n\nexport const registry = {\n    mfe1: () =&gt; loadRemoteModule({\n        type: &#039;module&#039;,\n        remoteEntry: &#039;http:\/\/localhost:4201\/remoteEntry.js&#039;,\n        exposedModule: &#039;.\/web-components&#039;\n    }),\n    mfe2: () =&gt; loadRemoteModule({\n        type: &#039;script&#039;,\n        remoteEntry: &#039;http:\/\/localhost:4202\/remoteEntry.js&#039;,\n        remoteName: &#039;mfe2&#039;,\n        exposedModule: &#039;.\/web-components&#039;\n    }),\n    mfe3: () =&gt; loadRemoteModule({\n        type: &#039;script&#039;,\n        remoteEntry: &#039;http:\/\/localhost:4203\/remoteEntry.js&#039;,\n        remoteName: &#039;mfe3&#039;,\n        exposedModule: &#039;.\/web-components&#039;\n    }),\n    mfe4: () =&gt; loadRemoteModule({\n        type: &#039;script&#039;,\n        remoteEntry: &#039;http:\/\/localhost:4204\/remoteEntry.js&#039;,\n        remoteName: &#039;mfe4&#039;,\n        exposedModule: &#039;.\/web-components&#039;\n    }),\n};<\/code><\/pre>\n<p>The calls needed for this example have been placed in the registry object shown. Please note that the first call is using <code>type: &#039;module&#039;<\/code> while the others are going with <code>type: &#039;script&#039;<\/code>. The reason is that the first one uses Angular 13.1 or higher. Beginning with Angular 13, the CLI emits EcmaScript modules instead of just &quot;plain old&quot; JavaScript files.<\/p>\n<p>As mfe2 and mfe3 are based on Angular 2 and mfe4 on a classical webpack build for React, we need to go with <code>type: &#039;script&#039;<\/code> here. In this case, we also have to specify the <code>remoteName<\/code> property which is defined in the remotes' webpack configurations.<\/p>\n<p>More about working with these methods can be found in my article on <a href=\"https:\/\/www.angulararchitects.io\/aktuelles\/dynamic-module-federation-with-angular\/\">Dynamic Federation<\/a>.<\/p>\n<p>To load the micro frontends, we just need to call the methods in the registry object:<\/p>\n<pre><code class=\"language-typescript\">const element = document.createElement(&#039;mfe1-element&#039;);\ndocument.body.appendChild(element);\n\nawait registry.mfe1();<\/code><\/pre>\n<p>After loading the web component, we can immediately use it. For this, we just need to add an element with the registered name.<\/p>\n<h3>Routing to Web Components<\/h3>\n<p>Of course, just loading our Micro Frontends and using them as dynamic web components is not enough. Our shell also needs to be able of routing to it.<\/p>\n<p>For routing to such a Web Component, the case study at hand uses a <code>WrapperComponent<\/code>:<\/p>\n<pre><code class=\"language-typescript\">@NgModule({\n  imports: [\n    BrowserModule,\n    RouterModule.forRoot([\n      { path: &#039;&#039;, component: HomeComponent, pathMatch: &#039;full&#039; },\n      { [...], component: WrapperComponent, data: { importName: &#039;mfe1&#039;, elementName: &#039;mfe1-element&#039; }},\n      { [...], component: WrapperComponent, data: { importName: &#039;mfe2&#039;, elementName: &#039;mfe2-element&#039; }},\n      { [...], component: WrapperComponent, data: { importName: &#039;mfe3&#039;, elementName: &#039;mfe3-element&#039; }},\n      { [...], component: WrapperComponent, data: { importName: &#039;mfe4&#039;, elementName: &#039;mfe4-element&#039; }},\n\n    ])\n  ],\n  declarations: [\n    AppComponent,\n    WrapperComponent\n  ],\n  providers: [],\n  bootstrap: [AppComponent]\n})\nexport class AppModule { }<\/code><\/pre>\n<p>To configure this wrapper, the router config's <code>data<\/code> property is used. It points to the remote's name mapped in the Module Federation config (<code>importName<\/code>) and to the respective Web Component's element name (<code>elementName<\/code>).<\/p>\n<p>The wrapper's implementation just loads the web component and adds it to a placeholder referenced with a <code>ViewChild<\/code>:<\/p>\n<pre><code class=\"language-typescript\">import { AfterContentInit, Component, ElementRef, OnInit, ViewChild, ViewContainerRef } from &#039;@angular\/core&#039;;\nimport { ActivatedRoute } from &#039;@angular\/router&#039;;\nimport { registry } from &#039;..\/registry&#039;;\n\n@Component({\n  template: &#039;&lt;div #vc&gt;&lt;\/div&gt;&#039;,\n})\nexport class WrapperComponent implements AfterContentInit {\n\n  @ViewChild(&#039;vc&#039;, {read: ElementRef, static: true})\n  vc: ElementRef;\n\n  constructor(private route: ActivatedRoute) { }\n\n  ngAfterContentInit(): void {\n\n    const elementName = this.route.snapshot.data[&#039;elementName&#039;];\n    const importName = this.route.snapshot.data[&#039;importName&#039;];\n\n    const importFn = registry[importName];\n    importFn()\n      .then(_ =&gt; console.debug(<code>element ${elementName} loaded!<\/code>))\n      .catch(err =&gt; console.error(<code>error loading ${elementName}:<\/code>, err));\n\n    const element = document.createElement(elementName);\n    this.vc.nativeElement.appendChild(element);\n\n  }\n\n}<\/code><\/pre>\n<p>As an <strong>alternative<\/strong>, we could make this example more dynamic by skipping the registry and passing the key data for loading the remote and creating its root element <strong>directly<\/strong> to the wrapper component.<\/p>\n<h3>No Need for a Separate Meta Framework<\/h3>\n<p>One of the best things of using Module Federation in general is that your framework -- in this case Angular -- does not even recognize that we are loading a separately compiled Micro Frontend. From Angular's perspective, this is just traditional lazy loading. Webpack Module Federation takes care of the heavy lifting underneath the covers. Hence, we don't need a separate meta framework and hence our scenario becomes easier.<\/p>\n<h3>Standalone Mode<\/h3>\n<p>All the Micro Frontends can also be started in standalone mode:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2020\/12\/standalone.png\" alt=\"Standalone Mode\" \/><\/p>\n<p>This is important because we want to develop, test, and deploy our Micro Frontends separately. <\/p>\n<h3>Lazy Loading within Micro Frontends<\/h3>\n<p>Another advantage of this approach is that we can use traditional lazy loading within each Micro Frontend. In cases where we load Micro Frontends directly without Module Federation this can cause issues because the Micro Frontend does not remember from which URL it was loaded and hence doesn't know where to find its lazy chunks.<\/p>\n<h2>The Bad<\/h2>\n<p>Now, let's proceed with some of the challenges this architecture comes with.<\/p>\n<h3>Bundle Size<\/h3>\n<p>Obviously, using several versions of the same framework but also using several frameworks together increases the bundle size. More stuff needs to be downloaded into the browser. However, if you have a lot of returning users, they will benefit from cache hits. <\/p>\n<p>Nevertheless, you need to respect the impact of increased bundles sizes when deciding for or against this architecture. While in some cases, this can for sure be neglected, e. g. in intranet scenarios, there are cases where this trade-off is not acceptable, e. g. in mobile scenarios or when the conversion rate is critical. Being able to run individual Micro Frontends in standalone mode, can help here for sure.<\/p>\n<h3>Several Routers must Work Together<\/h3>\n<p>In cases where the loaded Micro Frontends also use routing, we need to coordinate several routers: The shell's router and the router found in the individual Micro Frontends. <\/p>\n<p>Let's say, we have the URL <code>mfe1\/a<\/code>. In this case, the shell should only care about the first part, <code>mfe1<\/code> and route to the Web Component provided by Micro Frontend 1. The Micro Frontend in turn, should only respect the ending, <code>\/a<\/code> and activate the respective route. <\/p>\n<p>To achieve this, we could use <code>UrlMatcher<\/code> instead of concrete paths in the router configs. For instance, our shell is using the custom <code>UrlMather<\/code> <code>startsWith<\/code>:<\/p>\n<pre><code class=\"language-typescript\">@NgModule({\n  imports: [\n    BrowserModule,\n    RouterModule.forRoot([\n      { path: &#039;&#039;, component: HomeComponent, pathMatch: &#039;full&#039; },\n      { matcher: startsWith(&#039;mfe1&#039;), component: WrapperComponent, data: { importName: &#039;mfe1&#039;, elementName: &#039;mfe1-element&#039; }},\n      { matcher: startsWith(&#039;mfe2&#039;), component: WrapperComponent, data: { importName: &#039;mfe2&#039;, elementName: &#039;mfe2-element&#039; }},\n      { matcher: startsWith(&#039;mfe3&#039;), component: WrapperComponent, data: { importName: &#039;mfe3&#039;, elementName: &#039;mfe3-element&#039; }},\n      { matcher: startsWith(&#039;mfe4&#039;), component: WrapperComponent, data: { importName: &#039;mfe4&#039;, elementName: &#039;mfe4-element&#039; }},\n    ])\n  ],\n  declarations: [\n    AppComponent,\n    WrapperComponent\n  ],\n  providers: [],\n  bootstrap: [AppComponent]\n})\nexport class AppModule { }<\/code><\/pre>\n<p>All routes starting with <code>mfe1<\/code> will make the <code>WrapperComponent<\/code> loading <em>Micro Frontend 1<\/em>, for instance. The rest of the path is ignored by the shell and can be utilized by the Micro Frontend itself.<\/p>\n<p>This is what the implementation of <code>startsWith<\/code> looks like:<\/p>\n<pre><code class=\"language-typescript\">import { UrlMatcher, UrlSegment } from &#039;@angular\/router&#039;;\n\nexport function startsWith(prefix: string): UrlMatcher {\n    return (url: UrlSegment[]) =&gt; {\n        const fullUrl = url.map(u =&gt; u.path).join(&#039;\/&#039;);\n        if (fullUrl.startsWith(prefix)) {\n            return ({ consumed: url});\n        }\n        return null;\n    };\n}<\/code><\/pre>\n<p>In the Micro Frontends, however, our case study only analyzes the end of the route with a respective <code>endsWith<\/code> function: <\/p>\n<pre><code class=\"language-typescript\">@NgModule({\n  imports: [\n    BrowserModule,\n    RouterModule.forRoot([\n      { matcher: endsWith(&#039;a&#039;), component: AComponent},\n      { matcher: endsWith(&#039;b&#039;), component: BComponent},\n    ])\n  ],\n  declarations: [\n    AComponent,\n    BComponent,\n    AppComponent\n  ],\n  providers: [],\n  bootstrap: []\n})\nexport class AppModule {\n [...]\n}<\/code><\/pre>\n<p>Here is its implementation:<\/p>\n<pre><code class=\"language-typescript\">export function endsWith(prefix: string): UrlMatcher {\n    return (url: UrlSegment[]) =&gt; {\n        const fullUrl = url.map(u =&gt; u.path).join(&#039;\/&#039;);\n        if (fullUrl.endsWith(prefix)) {\n            return ({ consumed: url});\n        }\n        return null;\n    };\n}<\/code><\/pre>\n<h2>The Ugly<\/h2>\n<p>As all these frameworks are not designed to work side-by-side with different versions of itself or other frameworks, we also need some &quot;special&quot; workarounds. This section discusses them.<\/p>\n<h3>Bypassing Routing Issues<\/h3>\n<p>One issue that arises when using several Angular routers together, is that the inner routers don't recognize a route change. Hence, we need to &quot;invite them for routing&quot; manually every time the URL changes:<\/p>\n<pre><code class=\"language-typescript\">@Component([...])\nexport class AppComponent implements OnInit {\n\n  [...]\n\n  constructor(private router: Router) { }\n\n  ngOnInit(): void {\n    this.router.navigateByUrl(location.pathname.substr(1));\n    window.addEventListener(&#039;popstate&#039;, () =&gt; {\n      this.router.navigateByUrl(location.pathname.substr(1));\n    });\n  }\n}<\/code><\/pre>\n<p>For hash-based routing, we'd use <code>location.hash<\/code> and the <code>hashchanged<\/code> event.<\/p>\n<h3>Reuse Angular Platform<\/h3>\n<p>Per shared Angular version, we are only allowed to create one platform. To remember that there is already a shared platform for our version, we could put it into a global dictionary mapping the version number to the platform instance:<\/p>\n<pre><code class=\"language-typescript\">declare const require: any;\nconst ngVersion = require(&#039;..\/package.json&#039;).dependencies[&#039;@angular\/core&#039;]; \/\/ perhaps just take the major version \n\n(window as any).plattform = (window as any).plattform || {};\nlet platform = (window as any).plattform[ngVersion];\nif (!platform) {\n  platform = platformBrowser();\n  (window as any).plattform[ngVersion] = platform; \n}\nplatform.bootstrapModule(AppModule)\n  .catch(err =&gt; console.error(err));<\/code><\/pre>\n<h3>Angular Elements and Zone.js<\/h3>\n<p>The last one is a general one regarding Angular Elements: Even if we just load Zone.js once, we get several Zone.js instances: One for the shell and one per each Micro Frontend. This can lead to issues with change detection when data crosses the border of micro frontends. <\/p>\n<p>Of course, we could turn off Zone.js when bootstrapping the Micro Frontends:<\/p>\n<pre><code class=\"language-typescript\">platformBrowser()\n  .bootstrapModule(AppModule, { ngZone: &#039;noop&#039; }) <\/code><\/pre>\n<p>However, this also means we need to do change detection by hand. My GDE colleague, <a href=\"https:\/\/twitter.com\/tomastrajan\">Tomas Trajan<\/a> came up with another idea: Sharing one Zone.js instance. For this, the shell is grabbing the current <code>NgZone<\/code> instance and puts it into the global namespace:<\/p>\n<pre><code class=\"language-typescript\">export class AppModule {\n  constructor(private ngZone: NgZone) {\n    (window as any).ngZone = this.ngZone; \n  }\n}<\/code><\/pre>\n<p>All the Micro Frontends take it from there and reuse it when bootstrapping:<\/p>\n<pre><code class=\"language-typescript\">platformBrowser().bootstrapModule(AppModule, { ngZone: (window as any).ngZone }) <\/code><\/pre>\n<p>If this global <code>ngZone<\/code> property is undefined, the micro frontend's Angular instance uses a <code>ngZone<\/code> instance of its own. This is also the default behavior.  <\/p>\n<h2>Conclusion<\/h2>\n<p>Using Module Federation together with Web Components\/ Angular Elements leads to a huge amount of advantages: We can easily share libraries, provide and dynamically load Web Components and route to web components using a wrapper. Also, our main framework -- e. g. Angular -- also becomes our meta framework so that we don't need to deal with additional technologies. The loaded Web Components can even make use of lazy loading.<\/p>\n<p>However, this comes with costs: Bundle Sizes increase and we need several tricks and workarounds to make everything work seamlessly. <\/p>\n<p>In the past years I've helped numerous companies building Micro Frontend architectures using Web Components. Adding Module Federation to the game makes this by far simpler. Nevertheless, if you can somehow manage to just use one framework and version in your whole software system, using Module Federation is even more straightforward. <\/p>\n<h2>What's next? More on Architecture!<\/h2>\n<p>So far, we've seen that Module Federation is a strightforward solution for creating Micro Frontends on top of Angular. However, when dealing with it, several additional questions come in mind:<\/p>\n<ul>\n<li>According to which criteria can we sub-divide a huge application into micro frontends?<\/li>\n<li>Which access restrictions make sense?<\/li>\n<li>Which proven patterns should we use?<\/li>\n<li>How can we avoid pitfalls when working with Module Federation?<\/li>\n<li>Which advanced scenarios are possible?<\/li>\n<\/ul>\n<p>Our free eBook (about 100 pages) covers all these questions and more:<\/p>\n<p><a href=\"https:\/\/www.angulararchitects.io\/book\"><img decoding=\"async\" src=\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2020\/12\/cover-small.png\" alt=\"free ebook\"><\/a><\/p>\n<p>Feel free to <a href=\"https:\/\/www.angulararchitects.io\/book\">download it here<\/a> now!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Combining Module Federation and Web Components brings several advantages. But there are also some pitfalls we need workarounds for.<\/p>\n","protected":false},"author":9,"featured_media":4715,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"_price":"","_stock":"","_tribe_ticket_header":"","_tribe_default_ticket_provider":"","_ticket_start_date":"","_ticket_end_date":"","_tribe_ticket_show_description":"","_tribe_ticket_show_not_going":false,"_tribe_ticket_use_global_stock":"","_tribe_ticket_global_stock_level":"","_global_stock_mode":"","_global_stock_cap":"","_tribe_rsvp_for_event":"","_tribe_ticket_going_count":"","_tribe_ticket_not_going_count":"","_tribe_tickets_list":"[]","_tribe_ticket_has_attendee_info_fields":false,"footnotes":""},"categories":[1],"tags":[],"class_list":["post-4712","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-unkategorisiert"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.1.1 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Multi-Framework and -Version Micro Frontends with Module Federation: The Good, the Bad, the Ugly - ANGULARarchitects<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.angulararchitects.io\/en\/aktuelles\/multi-framework-and-version-micro-frontends-with-module-federation-the-good-the-bad-the-ugly\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Multi-Framework and -Version Micro Frontends with Module Federation: The Good, the Bad, the Ugly - ANGULARarchitects\" \/>\n<meta property=\"og:description\" content=\"Combining Module Federation and Web Components brings several advantages. But there are also some pitfalls we need workarounds for.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.angulararchitects.io\/en\/aktuelles\/multi-framework-and-version-micro-frontends-with-module-federation-the-good-the-bad-the-ugly\/\" \/>\n<meta property=\"og:site_name\" content=\"ANGULARarchitects\" \/>\n<meta property=\"article:published_time\" content=\"2021-06-01T20:13:52+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2020\/12\/multi-1.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"1280\" \/>\n\t<meta property=\"og:image:height\" content=\"670\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"Manfred Steyer, GDE\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@daniel\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Manfred Steyer, GDE\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"5 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/aktuelles\/multi-framework-and-version-micro-frontends-with-module-federation-the-good-the-bad-the-ugly\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/multi-framework-and-version-micro-frontends-with-module-federation-the-good-the-bad-the-ugly\/\"},\"author\":{\"name\":\"Manfred Steyer, GDE\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#\/schema\/person\/15628efa7af4475ffaaeeb26c5112951\"},\"headline\":\"Multi-Framework and -Version Micro Frontends with Module Federation: The Good, the Bad, the Ugly\",\"datePublished\":\"2021-06-01T20:13:52+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/multi-framework-and-version-micro-frontends-with-module-federation-the-good-the-bad-the-ugly\/\"},\"wordCount\":954,\"publisher\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#organization\"},\"image\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/aktuelles\/multi-framework-and-version-micro-frontends-with-module-federation-the-good-the-bad-the-ugly\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2020\/12\/title.jpg\",\"articleSection\":[\"Unkategorisiert\"],\"inLanguage\":\"en-US\"},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/multi-framework-and-version-micro-frontends-with-module-federation-the-good-the-bad-the-ugly\/\",\"url\":\"https:\/\/www.angulararchitects.io\/en\/aktuelles\/multi-framework-and-version-micro-frontends-with-module-federation-the-good-the-bad-the-ugly\/\",\"name\":\"Multi-Framework and -Version Micro Frontends with Module Federation: The Good, the Bad, the Ugly - ANGULARarchitects\",\"isPartOf\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/aktuelles\/multi-framework-and-version-micro-frontends-with-module-federation-the-good-the-bad-the-ugly\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/aktuelles\/multi-framework-and-version-micro-frontends-with-module-federation-the-good-the-bad-the-ugly\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2020\/12\/title.jpg\",\"datePublished\":\"2021-06-01T20:13:52+00:00\",\"breadcrumb\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/aktuelles\/multi-framework-and-version-micro-frontends-with-module-federation-the-good-the-bad-the-ugly\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.angulararchitects.io\/en\/aktuelles\/multi-framework-and-version-micro-frontends-with-module-federation-the-good-the-bad-the-ugly\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/aktuelles\/multi-framework-and-version-micro-frontends-with-module-federation-the-good-the-bad-the-ugly\/#primaryimage\",\"url\":\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2020\/12\/title.jpg\",\"contentUrl\":\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2020\/12\/title.jpg\",\"width\":1999,\"height\":1571},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/aktuelles\/multi-framework-and-version-micro-frontends-with-module-federation-the-good-the-bad-the-ugly\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/www.angulararchitects.io\/en\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Multi-Framework and -Version Micro Frontends with Module Federation: The Good, the Bad, the Ugly\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#website\",\"url\":\"https:\/\/www.angulararchitects.io\/en\/\",\"name\":\"ANGULARarchitects\",\"description\":\"AngularArchitects.io\",\"publisher\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/www.angulararchitects.io\/en\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#organization\",\"name\":\"ANGULARarchitects\",\"alternateName\":\"SOFTWAREarchitects\",\"url\":\"https:\/\/www.angulararchitects.io\/en\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2023\/07\/AA-Logo-RGB-horizontal-inside-knowledge-black.svg\",\"contentUrl\":\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2023\/07\/AA-Logo-RGB-horizontal-inside-knowledge-black.svg\",\"width\":644,\"height\":216,\"caption\":\"ANGULARarchitects\"},\"image\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/github.com\/angular-architects\",\"https:\/\/www.linkedin.com\/company\/angular-architects\/\"]},{\"@type\":\"Person\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#\/schema\/person\/15628efa7af4475ffaaeeb26c5112951\",\"name\":\"Manfred Steyer, GDE\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/a0b59539674d8b71ea1c1f4764b11244b5f499203f1d11b40f37d8f3f90be033?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/a0b59539674d8b71ea1c1f4764b11244b5f499203f1d11b40f37d8f3f90be033?s=96&d=mm&r=g\",\"caption\":\"Manfred Steyer, GDE\"},\"sameAs\":[\"https:\/\/x.com\/daniel\"]}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Multi-Framework and -Version Micro Frontends with Module Federation: The Good, the Bad, the Ugly - ANGULARarchitects","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.angulararchitects.io\/en\/aktuelles\/multi-framework-and-version-micro-frontends-with-module-federation-the-good-the-bad-the-ugly\/","og_locale":"en_US","og_type":"article","og_title":"Multi-Framework and -Version Micro Frontends with Module Federation: The Good, the Bad, the Ugly - ANGULARarchitects","og_description":"Combining Module Federation and Web Components brings several advantages. But there are also some pitfalls we need workarounds for.","og_url":"https:\/\/www.angulararchitects.io\/en\/aktuelles\/multi-framework-and-version-micro-frontends-with-module-federation-the-good-the-bad-the-ugly\/","og_site_name":"ANGULARarchitects","article_published_time":"2021-06-01T20:13:52+00:00","og_image":[{"width":1280,"height":670,"url":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2020\/12\/multi-1.jpg","type":"image\/jpeg"}],"author":"Manfred Steyer, GDE","twitter_card":"summary_large_image","twitter_creator":"@daniel","twitter_misc":{"Written by":"Manfred Steyer, GDE","Est. reading time":"5 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.angulararchitects.io\/en\/aktuelles\/multi-framework-and-version-micro-frontends-with-module-federation-the-good-the-bad-the-ugly\/#article","isPartOf":{"@id":"https:\/\/www.angulararchitects.io\/en\/blog\/multi-framework-and-version-micro-frontends-with-module-federation-the-good-the-bad-the-ugly\/"},"author":{"name":"Manfred Steyer, GDE","@id":"https:\/\/www.angulararchitects.io\/en\/#\/schema\/person\/15628efa7af4475ffaaeeb26c5112951"},"headline":"Multi-Framework and -Version Micro Frontends with Module Federation: The Good, the Bad, the Ugly","datePublished":"2021-06-01T20:13:52+00:00","mainEntityOfPage":{"@id":"https:\/\/www.angulararchitects.io\/en\/blog\/multi-framework-and-version-micro-frontends-with-module-federation-the-good-the-bad-the-ugly\/"},"wordCount":954,"publisher":{"@id":"https:\/\/www.angulararchitects.io\/en\/#organization"},"image":{"@id":"https:\/\/www.angulararchitects.io\/en\/aktuelles\/multi-framework-and-version-micro-frontends-with-module-federation-the-good-the-bad-the-ugly\/#primaryimage"},"thumbnailUrl":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2020\/12\/title.jpg","articleSection":["Unkategorisiert"],"inLanguage":"en-US"},{"@type":"WebPage","@id":"https:\/\/www.angulararchitects.io\/en\/blog\/multi-framework-and-version-micro-frontends-with-module-federation-the-good-the-bad-the-ugly\/","url":"https:\/\/www.angulararchitects.io\/en\/aktuelles\/multi-framework-and-version-micro-frontends-with-module-federation-the-good-the-bad-the-ugly\/","name":"Multi-Framework and -Version Micro Frontends with Module Federation: The Good, the Bad, the Ugly - ANGULARarchitects","isPartOf":{"@id":"https:\/\/www.angulararchitects.io\/en\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.angulararchitects.io\/en\/aktuelles\/multi-framework-and-version-micro-frontends-with-module-federation-the-good-the-bad-the-ugly\/#primaryimage"},"image":{"@id":"https:\/\/www.angulararchitects.io\/en\/aktuelles\/multi-framework-and-version-micro-frontends-with-module-federation-the-good-the-bad-the-ugly\/#primaryimage"},"thumbnailUrl":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2020\/12\/title.jpg","datePublished":"2021-06-01T20:13:52+00:00","breadcrumb":{"@id":"https:\/\/www.angulararchitects.io\/en\/aktuelles\/multi-framework-and-version-micro-frontends-with-module-federation-the-good-the-bad-the-ugly\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.angulararchitects.io\/en\/aktuelles\/multi-framework-and-version-micro-frontends-with-module-federation-the-good-the-bad-the-ugly\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.angulararchitects.io\/en\/aktuelles\/multi-framework-and-version-micro-frontends-with-module-federation-the-good-the-bad-the-ugly\/#primaryimage","url":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2020\/12\/title.jpg","contentUrl":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2020\/12\/title.jpg","width":1999,"height":1571},{"@type":"BreadcrumbList","@id":"https:\/\/www.angulararchitects.io\/en\/aktuelles\/multi-framework-and-version-micro-frontends-with-module-federation-the-good-the-bad-the-ugly\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.angulararchitects.io\/en\/"},{"@type":"ListItem","position":2,"name":"Multi-Framework and -Version Micro Frontends with Module Federation: The Good, the Bad, the Ugly"}]},{"@type":"WebSite","@id":"https:\/\/www.angulararchitects.io\/en\/#website","url":"https:\/\/www.angulararchitects.io\/en\/","name":"ANGULARarchitects","description":"AngularArchitects.io","publisher":{"@id":"https:\/\/www.angulararchitects.io\/en\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.angulararchitects.io\/en\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/www.angulararchitects.io\/en\/#organization","name":"ANGULARarchitects","alternateName":"SOFTWAREarchitects","url":"https:\/\/www.angulararchitects.io\/en\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.angulararchitects.io\/en\/#\/schema\/logo\/image\/","url":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2023\/07\/AA-Logo-RGB-horizontal-inside-knowledge-black.svg","contentUrl":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2023\/07\/AA-Logo-RGB-horizontal-inside-knowledge-black.svg","width":644,"height":216,"caption":"ANGULARarchitects"},"image":{"@id":"https:\/\/www.angulararchitects.io\/en\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/github.com\/angular-architects","https:\/\/www.linkedin.com\/company\/angular-architects\/"]},{"@type":"Person","@id":"https:\/\/www.angulararchitects.io\/en\/#\/schema\/person\/15628efa7af4475ffaaeeb26c5112951","name":"Manfred Steyer, GDE","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.angulararchitects.io\/en\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/a0b59539674d8b71ea1c1f4764b11244b5f499203f1d11b40f37d8f3f90be033?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/a0b59539674d8b71ea1c1f4764b11244b5f499203f1d11b40f37d8f3f90be033?s=96&d=mm&r=g","caption":"Manfred Steyer, GDE"},"sameAs":["https:\/\/x.com\/daniel"]}]}},"_links":{"self":[{"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/posts\/4712","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/users\/9"}],"replies":[{"embeddable":true,"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/comments?post=4712"}],"version-history":[{"count":0,"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/posts\/4712\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/media\/4715"}],"wp:attachment":[{"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/media?parent=4712"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/categories?post=4712"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/tags?post=4712"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}