{"id":5187,"date":"2021-07-23T20:44:12","date_gmt":"2021-07-23T18:44:12","guid":{"rendered":"https:\/\/www.angulararchitects.io\/?p=5187"},"modified":"2024-01-19T15:21:14","modified_gmt":"2024-01-19T14:21:14","slug":"pitfalls-with-module-federation-and-angular","status":"publish","type":"post","link":"https:\/\/www.angulararchitects.io\/en\/blog\/pitfalls-with-module-federation-and-angular\/","title":{"rendered":"Pitfalls with Module Federation and Angular"},"content":{"rendered":"<div class=\"wp-post-series-box series-module-federation wp-post-series-box--expandable\">\n\t\t\t<input id=\"collapsible-series-module-federation6a1423522b455\" class=\"wp-post-series-box__toggle_checkbox\" type=\"checkbox\">\n\t\n\t<label\n\t\tclass=\"wp-post-series-box__label\"\n\t\t\t\t\tfor=\"collapsible-series-module-federation6a1423522b455\"\n\t\t\ttabindex=\"0\"\n\t\t\t\t>\n\t\t<p class=\"wp-post-series-box__name wp-post-series-name\">\n\t\t\tThis is post 7 of 10 in the series <em>&ldquo;Module Federation&rdquo;<\/em>\t\t<\/p>\n\t\t\t<\/label>\n\n\t\t\t<div class=\"wp-post-series-box__posts\">\n\t\t\t<ol>\n\t\t\t\t\t\t\t\t\t<li><a href=\"https:\/\/www.angulararchitects.io\/en\/blog\/the-microfrontend-revolution-module-federation-in-webpack-5\/\">The Microfrontend Revolution: Module Federation in Webpack 5<\/a><\/li>\n\t\t\t\t\t\t\t\t\t<li><a href=\"https:\/\/www.angulararchitects.io\/en\/blog\/the-microfrontend-revolution-part-2-module-federation-with-angular\/\">The Microfrontend Revolution: Module Federation with Angular<\/a><\/li>\n\t\t\t\t\t\t\t\t\t<li><a href=\"https:\/\/www.angulararchitects.io\/en\/blog\/dynamic-module-federation-with-angular\/\">Dynamic Module Federation with Angular<\/a><\/li>\n\t\t\t\t\t\t\t\t\t<li><a href=\"https:\/\/www.angulararchitects.io\/en\/blog\/dynamic-module-federation-with-angular-2\/\">Building A Plugin-based Workflow Designer With Angular and Module Federation<\/a><\/li>\n\t\t\t\t\t\t\t\t\t<li><a href=\"https:\/\/www.angulararchitects.io\/en\/blog\/getting-out-of-version-mismatch-hell-with-module-federation\/\">Getting Out of Version-Mismatch-Hell with Module Federation<\/a><\/li>\n\t\t\t\t\t\t\t\t\t<li><a href=\"https:\/\/www.angulararchitects.io\/en\/blog\/using-module-federation-with-monorepos-and-angular\/\">Using Module Federation with (Nx) Monorepos and Angular<\/a><\/li>\n\t\t\t\t\t\t\t\t\t<li><span class=\"wp-post-series-box__current\">Pitfalls with Module Federation and Angular<\/span><\/li>\n\t\t\t\t\t\t\t\t\t<li><a href=\"https:\/\/www.angulararchitects.io\/en\/blog\/multi-framework-and-version-micro-frontends-with-module-federation-your-4-steps-guide\/\">Multi-Framework and -Version Micro Frontends with Module Federation: Your 4 Steps Guide<\/a><\/li>\n\t\t\t\t\t\t\t\t\t<li><a href=\"https:\/\/www.angulararchitects.io\/en\/blog\/module-federation-with-angulars-standalone-components\/\">Module Federation with Angular&#8217;s Standalone Components<\/a><\/li>\n\t\t\t\t\t\t\t\t\t<li><a href=\"https:\/\/www.angulararchitects.io\/en\/blog\/whats-new-in-angular-architects-module-federation-14-3\/\">What&#8217;s New in our Module Federation Plugin 14.3?<\/a><\/li>\n\t\t\t\t\t\t\t<\/ol>\n\t\t<\/div>\n\t<\/div>\n<p>In this article, I'm going to destroy my Module Federation example! However, you don't need to worry: It's for a very good reason. The goal is to show typical pitfalls that come up when using Module Federation together with Angular. Also, this articles presents some strategies for avoiding these pitfalls. <\/p>\n<p>While Module Federation is really a straight and thoroughly thought through solution, using Micro Frontends means in general to make runtime dependencies out of compile time dependencies. As a result, the compiler cannot protect you as well as you are used to. <\/p>\n<p>If you want to try out the examples used here, you can fork this <a href=\"https:\/\/github.com\/manfredsteyer\/module-federation-plugin-example.git\">example<\/a>. <\/p>\n<h2>&quot;No required version specified&quot; and Secondary Entry Points<\/h2>\n<p>For the first pitfall I want to talk about, let's have a look to our <code>shell<\/code>'s <code>webpack.config.js<\/code>. Also, let's simplify the <code>shared<\/code> node as follows:<\/p>\n<pre><code class=\"language-typescript\"> shared: {\n   &quot;@angular\/core&quot;: { singleton: true, strictVersion: true },\n   &quot;@angular\/common&quot;: { singleton: true, strictVersion: true },\n   &quot;@angular\/router&quot;: { singleton: true, strictVersion: true },\n   &quot;@angular\/common\/http&quot;: { singleton: true, strictVersion: true }, \n },<\/code><\/pre>\n<p>As you see, we don't specify a <code>requiredVersion<\/code> anymore. Normally this is not required because webpack Module Federation is very smart with finding out which version you use.<\/p>\n<p>However, now, when compiling the shell  (<code>ng build shell<\/code>), we get the following error:<\/p>\n<blockquote>\n<p>shared module @angular\/common - Warning: No required version specified and unable to automatically determine one. Unable to find required version for &quot;@angular\/common&quot; in description file (C:\\Users\\Manfred\\Documents\\artikel\\ModuleFederation-Pitfalls\\example\\node_modules\\@angular\\common\\package.json). It need to be in dependencies, devDependencies or peerDependencies.<\/p>\n<\/blockquote>\n<p>The reason for this is the secondary entry point <code>@angular\/common\/http<\/code> which is a bit like an npm package within an npm package. Technically, it's just another file exposed by the npm package <code>@angular\/common<\/code>. <\/p>\n<p>Unsurprisingly, <code>@angular\/common\/http<\/code> uses <code>@angular\/common<\/code> and webpack recognizes this. For this reason, webpack wants to find out which version of <code>@angular\/common<\/code> is used. For this, it looks into the npm package's <code>package.json<\/code> (<code>@angular\/common\/package.json<\/code>) and browses the dependencies there. However, <code>@angular\/common<\/code> itself is not a dependency of <code>@angular\/common<\/code> and hence, the version cannot be found.<\/p>\n<p>You will have the same challenge with other packages using secondary entry points, e. g. <code>@angular\/material<\/code>.<\/p>\n<p>To avoid this situation, you can assign versions to all shared libraries by hand:<\/p>\n<pre><code class=\"language-typescript\"> shared: {\n   &quot;@angular\/core&quot;: { singleton: true, strictVersion: true, requiredVersion: &#039;12.0.0&#039; },\n   &quot;@angular\/common&quot;: { singleton: true, strictVersion: true, requiredVersion: &#039;12.0.0&#039; },\n   &quot;@angular\/router&quot;: { singleton: true, strictVersion: true, requiredVersion: &#039;12.0.0&#039; },\n   &quot;@angular\/common\/http&quot;: { singleton: true, strictVersion: true, requiredVersion: &#039;12.0.0&#039; }, \n },<\/code><\/pre>\n<p>Obviously, this is cumbersome and so we came up with another solution. Since version 12.3, <a href=\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2021\/07\/https:\/\/www.npmjs.com\/package\/@angular-architects\/module-federation\">@angular-architects\/module-federation<\/a> comes with an unspectacular looking helper function called <code>shared<\/code>. If your <code>webpack.config.js<\/code> was generated with this or a newer version, it already uses this helper function.<\/p>\n<pre><code class=\"language-typescript\"> [...]\n\n const mf = require(&quot;@angular-architects\/module-federation\/webpack&quot;);\n [...]\n const share = mf.share;\n\n [...]\n\n shared: share({\n   &quot;@angular\/core&quot;: { singleton: true, strictVersion: true, requiredVersion: &#039;auto&#039; },\n   &quot;@angular\/common&quot;: { singleton: true, strictVersion: true, requiredVersion: &#039;auto&#039; },\n   &quot;@angular\/router&quot;: { singleton: true, strictVersion: true, requiredVersion: &#039;auto&#039; },\n   &quot;@angular\/common\/http&quot;: { singleton: true, strictVersion: true, requiredVersion: &#039;auto&#039; }, \n   &quot;@angular\/material\/snack-bar&quot;: { singleton: true, strictVersion: true, requiredVersion:&#039;auto&#039; }, \n\n })<\/code><\/pre>\n<p>As you see here, the <code>share<\/code> function wraps the object with the shared libraries. It allows to use <code>requiredVersion: &#039;auto&#039;<\/code> and converts the value <code>auto<\/code> to the value found in your shell's (or micro frontend's) <code>package.json<\/code>.<\/p>\n<h2>Unobvious Version Mismatches: Issues with Peer Dependencies<\/h2>\n<p>Have you ever just ignored a peer dependency warning? Honestly, I think we all know such situations. And ignoring them is often okay-ish as we know, at runtime everything will be fine. Unfortunately, such a situation can confuses webpack Module Federation when trying to auto-detect the needed versions of peer dependencies. <\/p>\n<p>To demonstrate this situation, let's install <code>@angular\/material<\/code> and <code>@angular\/cdk<\/code> in a version that is at least 2 versions behind our Angular version. In this case, we should get a peer dependency warnings.<\/p>\n<p>In my case this is done as follows:<\/p>\n<pre><code class=\"language-json\">npm i @angular\/material@10\n npm i @angular\/cdk@10 <\/code><\/pre>\n<p>Now, let's switch to the Micro Frontend's (<code>mfe1<\/code>) <code>FlightModule<\/code> to import the <code>MatSnackBarModule<\/code>:<\/p>\n<pre><code class=\"language-typescript\"> [...]\n import { MatSnackBarModule  } from &#039;@angular\/material\/snack-bar&#039;;\n [...]\n\n @NgModule({\n   imports: [\n     [...]\n     \/\/ Add this line\n     MatSnackBarModule,\n   ],\n   declarations: [\n     [...]  \n   ]\n })\n export class FlightsModule { }<\/code><\/pre>\n<p>To make use of the snack bar in the <code>FlightsSearchComponent<\/code>, inject it into its constructor and call its <code>open<\/code> method:<\/p>\n<pre><code class=\"language-typescript\"> [...]\n import { MatSnackBar } from &#039;@angular\/material\/snack-bar&#039;;\n\n @Component({\n   selector: &#039;app-flights-search&#039;,\n   templateUrl: &#039;.\/flights-search.component.html&#039;\n })\n export class FlightsSearchComponent {\n   constructor(snackBar: MatSnackBar) {\n     snackBar.open(&#039;Hallo Welt!&#039;);\n   }\n }<\/code><\/pre>\n<p>Also, for this experiment, make sure the <code>webpack.config.js<\/code> in the project <code>mfe1<\/code> does <strong>not<\/strong> define the versions of the dependencies shared:<\/p>\n<pre><code class=\"language-typescript\"> shared: {\n   &quot;@angular\/core&quot;: { singleton: true, strictVersion: true },\n   &quot;@angular\/common&quot;: { singleton: true, strictVersion: true },\n   &quot;@angular\/router&quot;: { singleton: true, strictVersion: true },\n   &quot;@angular\/common\/http&quot;: { singleton: true, strictVersion: true }, \n },<\/code><\/pre>\n<p>Not defining these versions by hand forces Module Federation into trying to detect them automatically. However, the peer dependency conflict gives Module Federation a hard time and so it brings up the following error:  <\/p>\n<blockquote>\n<p>Unsatisfied version 12.0.0 of shared singleton module @angular\/core (required ^10.0.0 || ^11.0.0-0) ; Zone: <root> ; Task: Promise.then ; Value: Error: Unsatisfied version 12.0.0 of shared singleton module @angular\/core (required ^10.0.0 || ^11.0.0-0)<\/p>\n<\/blockquote>\n<p>While <code>@angular\/material<\/code> and <code>@angular\/cdk<\/code> officially need <code>@angular\/core<\/code> 10, the rest of the application already uses <code>@angular\/core<\/code> 12. This shows that webpack looks into the <code>package.json<\/code> files of all the shared dependencies for determining the needed versions.<\/p>\n<p>In order to resolve this, you can set the versions by hand or by using the helper function <code>share<\/code> that uses the version found in your project's <code>package.json<\/code>:<\/p>\n<pre><code class=\"language-typescript\"> [...]\n\n const mf = require(&quot;@angular-architects\/module-federation\/webpack&quot;);\n [...]\n const share = mf.share;\n\n [...]\n\n shared: share({\n   &quot;@angular\/core&quot;: { singleton: true, strictVersion: true, requiredVersion: &#039;auto&#039; },\n   &quot;@angular\/common&quot;: { singleton: true, strictVersion: true, requiredVersion: &#039;auto&#039; },\n   &quot;@angular\/router&quot;: { singleton: true, strictVersion: true, requiredVersion: &#039;auto&#039; },\n   &quot;@angular\/common\/http&quot;: { singleton: true, strictVersion: true, requiredVersion: &#039;auto&#039; }, \n   &quot;@angular\/material\/snack-bar&quot;: { singleton: true, strictVersion: true, requiredVersion:&#039;auto&#039; }, \n })<\/code><\/pre>\n<h2>Issues with Sharing Code and Data<\/h2>\n<p>In our example, the <code>shell<\/code> and the micro frontend <code>mfe1<\/code> share the <code>auth-lib<\/code>. Its <code>AuthService<\/code> stores the current user name. Hence, the <code>shell<\/code> can set the user name and the lazy loaded <code>mfe1<\/code> can access it:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2021\/07\/sharing-via-auth-lib.png\" alt=\"Sharing User Name\" \/><\/p>\n<p>If <code>auth-lib<\/code> was a traditional npm package, we could just register it as a shared library with module federation. However, in our case, the <code>auth-lib<\/code> is just a library in our monorepo. And libraries in that sense are just folders with source code. <\/p>\n<p>To make this folder look like a npm package, there is a path mapping for it in the <code>tsconfig.json<\/code>:<\/p>\n<pre><code class=\"language-json\"> &quot;paths&quot;: {\n   &quot;auth-lib&quot;: [\n     &quot;projects\/auth-lib\/src\/public-api.ts&quot;\n   ]\n }<\/code><\/pre>\n<p>Please note that we are directly pointing to the <code>src<\/code> folder of the <code>auth-lib<\/code>. Nx does this by default. If you go with a traditional CLI project, you need to adjust this by hand.<\/p>\n<p>Fortunately, Module Federation got us covered with such scenarios. To make configuring such cases a bit easier as well as to prevent issues with the Angular compiler, <code>@angular-architects\/module-federation<\/code> provides a configuration property called:<\/p>\n<pre><code class=\"language-typescript\"> module.exports = withModuleFederationPlugin({\n\n     \/\/ Shared packages:\n     shared: [...],\n\n     \/\/ Explicitly share mono-repo libs:\n     sharedMappings: [&#039;auth-lib&#039;],\n\n });<\/code><\/pre>\n<blockquote>\n<p><strong>Important:<\/strong> Since Version 14.3, the withModuleFederationPlugin helper automatically shares <strong>all<\/strong> mapped paths if you don't use the property <code>sharedMappings<\/code> at all. Hence, the issue described here, will not happen.<\/p>\n<\/blockquote>\n<p>Obviously, if you don't opt-in into sharing the library in one of the projects, these project will get their own copy of the <code>auth-lib<\/code> and hence sharing the user name isn't possible anymore. <\/p>\n<p>However, there is a constellation with the same underlying issue that is everything but obvious. To construct this situation, let's add another library to our monorepo:<\/p>\n<pre><code class=\"language-json\">ng g lib other-lib<\/code><\/pre>\n<p>Also, make sure we have a path mapping for it pointing to its source code:<\/p>\n<pre><code class=\"language-json\"> &quot;paths&quot;: {\n   &quot;other-lib&quot;: [\n     &quot;projects\/other-lib\/src\/public-api.ts&quot;\n   ],\n }<\/code><\/pre>\n<p>Let's assume we also want to store the current user name in this library:<\/p>\n<pre><code class=\"language-typescript\"> import { Injectable } from &#039;@angular\/core&#039;;\n\n @Injectable({\n   providedIn: &#039;root&#039;\n })\n export class OtherLibService {\n\n   \/\/ Add this:\n   userName: string;\n\n   constructor() { }\n\n }<\/code><\/pre>\n<p>And let's also assume, the <code>AuthLibService<\/code> delegates to this property:<\/p>\n<pre><code class=\"language-typescript\"> import { Injectable } from &#039;@angular\/core&#039;;\n import { OtherLibService } from &#039;other-lib&#039;;\n\n @Injectable({\n   providedIn: &#039;root&#039;\n })\n export class AuthLibService {\n\n   private userName: string;\n\n   public get user(): string {\n     return this.userName;\n   }\n\n   public get otherUser(): string {\n     \/\/ DELEGATION!\n     return this.otherService.userName;\n   }\n\n   constructor(private otherService: OtherLibService) { }\n\n   public login(userName: string, password: string): void {\n     \/\/ Authentication for **honest** users TM. (c) Manfred Steyer\n     this.userName = userName;\n\n     \/\/ DELEGATION!\n     this.otherService.userName = userName;\n   }\n\n }<\/code><\/pre>\n<p>The shell's <code>AppComponent<\/code> is just calling the <code>login<\/code> method:<\/p>\n<pre><code class=\"language-typescript\"> import { Component } from &#039;@angular\/core&#039;;\n import { AuthLibService } from &#039;auth-lib&#039;;\n\n @Component({\n   selector: &#039;app-root&#039;,\n   templateUrl: &#039;.\/app.component.html&#039;\n })\n export class AppComponent {\n   title = &#039;shell&#039;;\n\n   constructor(\n     private service: AuthLibService\n     ) {\n\n     this.service.login(&#039;Max&#039;, null);\n   }\n\n }<\/code><\/pre>\n<p>However, now the Micro Frontend has three ways of getting the defined user name:<\/p>\n<pre><code class=\"language-typescript\"> import { HttpClient } from &#039;@angular\/common\/http&#039;;\n import {Component} from &#039;@angular\/core&#039;;\n import { AuthLibService } from &#039;auth-lib&#039;;\n import { OtherLibService } from &#039;other-lib&#039;;\n\n @Component({\n   selector: &#039;app-flights-search&#039;,\n   templateUrl: &#039;.\/flights-search.component.html&#039;\n })\n export class FlightsSearchComponent {\n   constructor(\n     authService: AuthLibService,\n     otherService: OtherLibService) {\n\n     \/\/ Three options for getting the user name:\n     console.log(&#039;user from authService&#039;, authService.user);\n     console.log(&#039;otherUser from authService&#039;, authService.otherUser);\n     console.log(&#039;otherUser from otherService&#039;, otherService.userName);\n\n   }\n }<\/code><\/pre>\n<p>At first sight, all these three options should bring up the same value. However, if we only share <code>auth-lib<\/code> <strong>but not<\/strong> <code>other-lib<\/code>, we get the following result:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2021\/07\/sharing-issue.png\" alt=\"Issue with sharing libs\" \/><\/p>\n<p>As <code>other-lib<\/code> is not shared, both, <code>auth-lib<\/code> but also the micro frontend get their very own version of it. Hence, we have two instances of it in place here. While the first one knows the user name, the second one doesn't.<\/p>\n<p>What can we learn from this? Well, it would be a good idea to also share the dependencies of our shared libraries (regardless of sharing libraries in a monorepo or traditional npm packages!).<\/p>\n<p>This also holds true for secondary entry points our shared libraries belong to.<\/p>\n<p><em>Hint:<\/em> <code>@angular-architects\/module-federation<\/code> comes with a helper function <code>shareAll<\/code> for sharing all dependencies defined in your project's <code>package.json<\/code>:<\/p>\n<pre><code class=\"language-typescript\"> shared: {\n   ...shareAll({ \n       singleton: true, \n       strictVersion: true, \n       requiredVersion: &#039;auto&#039; \n   }),\n }<\/code><\/pre>\n<p>This can at least lower the pain in such cases for prototyping. Also, you can make <code>share<\/code> and <code>shareAll<\/code> to include all secondary entry points by using the property <code>includeSecondaries<\/code>:<\/p>\n<pre><code class=\"language-typescript\"> shared: share({\n     &quot;@angular\/common&quot;: { \n         singleton: true, \n         strictVersion: true,\n         requiredVersion: &#039;auto&#039;,\n         includeSecondaries: {\n             skip: [&#039;@angular\/http\/testing&#039;]\n         }\n     },\n     [...]\n })<\/code><\/pre>\n<h2>NullInjectorError: Service expected in Parent Scope (Root Scope)<\/h2>\n<p>Okay, the last section was a bit difficult. Hence, let's proceed with an easier one. Perhaps you've seen an error like this here:<\/p>\n<pre><code class=\"language-json\">ERROR Error: Uncaught (in promise): NullInjectorError: R3InjectorError(FlightsModule)[HttpClient -&gt; HttpClient -&gt; HttpClient -&gt; HttpClient]: \n   NullInjectorError: No provider for HttpClient!\n NullInjectorError: R3InjectorError(FlightsModule)[HttpClient -&gt; HttpClient -&gt; HttpClient -&gt; HttpClient]: \n   NullInjectorError: No provider for HttpClient!<\/code><\/pre>\n<p>It seems like, the loaded Micro Frontend <code>mfe1<\/code> cannot get hold of the <code>HttpClient<\/code>. Perhaps it even works when running <code>mfe1<\/code> in standalone mode.<\/p>\n<p>The reason for this is very likely that we are not exposing the whole Micro Frontend via Module Federation but only selected parts, e. g. some Features Modules with Child Routes:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2021\/07\/shell-mfe1.png\" alt=\"Feature Modules exposed via Module Federation\" \/><\/p>\n<p>Or to put it in another way: <strong>DO NOT<\/strong> expose the Micro Frontend's <code>AppModule<\/code>. However, if we expect the <code>AppModule<\/code> to provide some global services like the <code>HttpClient<\/code>, we also need to do this in the shell's <code>AppModule<\/code>:<\/p>\n<pre><code class=\"language-typescript\"> \/\/ Shell&#039;s AppModule\n @NgModule({\n   imports: [\n     [...]\n     \/\/ Provide global services your micro frontends expect:\n     HttpClientModule,\n   ],\n   [...]\n })\n export class AppModule { }<\/code><\/pre>\n<h2>Several Root Scopes<\/h2>\n<p>In a very simple scenario you might try to just expose the Micro Frontend's <code>AppModule<\/code>.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2021\/07\/appmod-appmod.png\" alt=\"AppModule loads exposed AppModule\" \/><\/p>\n<p>As you see here, now, the shell's <code>AppModule<\/code> uses the Micro Frontend's <code>AppModule<\/code>. If you use the router, you will get some initial issues because you need to call <code>RouterModule.forRoot<\/code> for each <code>AppModule<\/code> (Root Module) on the one side while you are only allowed to call it once on the other side.<\/p>\n<p>But if you just shared components or services, this might work at first sight. However, the actual issue here is that Angular creates a root scope for each root module. Hence, we have two root scopes now. This is something no one expects. <\/p>\n<p>Also, this duplicates all shared services registered for the root scope, e. g. with <code>providedIn: &#039;root&#039;<\/code>. Hence, both, the shell and the Micro Frontend have their very own copy of these services and this is something, no one is expecting.<\/p>\n<p>A <strong>simple but also non preferable solutions<\/strong> is to put your shared services into the <code>platform<\/code> scope:<\/p>\n<pre><code class=\"language-typescript\"> \/\/ Don&#039;t do this at home!\n @Injectable({\n   providedIn: &#039;platform&#039;\n })\n export class AuthLibService {\n }<\/code><\/pre>\n<p>However, normally, this scope is intended to be used by Angular-internal stuff. Hence, the only clean solution here is to not share your <code>AppModule<\/code> but only lazy feature modules. By using this practice, you assure (more or less) that these feature modules work the same when loaded into the shell as when used in standalone-mode.<\/p>\n<h2>Different Versions of Angular<\/h2>\n<p>Another, less obvious pitfall you can run into is this one here:<\/p>\n<pre><code class=\"language-json\"> node_modules_angular_core___ivy_ngcc___fesm2015_core_js.js:6850 ERROR Error: Uncaught (in promise): Error: inject() must be called from an injection context\n Error: inject() must be called from an injection context\n     at pr (node_modules_angular_core___ivy_ngcc___fesm2015_core_js.2fc3951af86e4bae0c59.js:1)\n     at gr (node_modules_angular_core___ivy_ngcc___fesm2015_core_js.2fc3951af86e4bae0c59.js:1)\n     at Object.e.\u0275fac [as factory] (node_modules_angular_core___ivy_ngcc___fesm2015_core_js.2fc3951af86e4bae0c59.js:1)\n     at R3Injector.hydrate (node_modules_angular_core___ivy_ngcc___fesm2015_core_js.js:11780)\n     at R3Injector.get (node_modules_angular_core___ivy_ngcc___fesm2015_core_js.js:11600)\n     at node_modules_angular_core___ivy_ngcc___fesm2015_core_js.js:11637\n     at Set.forEach (&lt;anonymous&gt;)\n     at R3Injector._resolveInjectorDefTypes (node_modules_angular_core___ivy_ngcc___fesm2015_core_js.js:11637)\n     at new NgModuleRef$1 (node_modules_angular_core___ivy_ngcc___fesm2015_core_js.js:25462)\n     at NgModuleFactory$1.create (node_modules_angular_core___ivy_ngcc___fesm2015_core_js.js:25516)\n     at resolvePromise (polyfills.js:10658)\n     at resolvePromise (polyfills.js:10610)\n     at polyfills.js:10720\n     at ZoneDelegate.invokeTask (polyfills.js:10247)\n     at Object.onInvokeTask (node_modules_angular_core___ivy_ngcc___fesm2015_core_js.js:28753)\n     at ZoneDelegate.invokeTask (polyfills.js:10246)\n     at Zone.runTask (polyfills.js:10014)\n     at drainMicroTaskQueue (polyfills.js:10427)<\/code><\/pre>\n<p>With <code>inject() must be called from an injection context<\/code> Angular tells us that there are several Angular versions loaded at once.<\/p>\n<p>To provoke this error, adjust your shell's <code>webpack.config.js<\/code> as follows:<\/p>\n<pre><code class=\"language-typescript\">shared: share({\n   &quot;@angular\/core&quot;: { requiredVersion: &#039;auto&#039; },\n   &quot;@angular\/common&quot;: { requiredVersion: &#039;auto&#039; },\n   &quot;@angular\/router&quot;: { requiredVersion: &#039;auto&#039; },\n   &quot;@angular\/common\/http&quot;: { requiredVersion: &#039;auto&#039; }, \n })<\/code><\/pre>\n<p>Please note, that these libraries are not configured to be singletons anymore. Hence, Module Federation allows loading several versions of them if there is no <a href=\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2021\/07\/https:\/\/www.angulararchitects.io\/aktuelles\/getting-out-of-version-mismatch-hell-with-module-federation\/\">highest compatible version<\/a>. <\/p>\n<p>Also, you have to know that the shell's <code>package.json<\/code> points to Angular 12.0.0 <em>without<\/em> ^ or ~, hence we exactly need this very version. <\/p>\n<p>If we load a Micro Frontend that uses a different Angular version, Module Federation falls back to loading Angular twice, once the version for the shell and once the version for the Micro Frontend. You can try this out by updating the shell's <code>app.routes.ts<\/code> as follows:<\/p>\n<pre><code class=\"language-typescript\"> {\n   path: &#039;flights&#039;,\n   loadChildren: () =&gt; loadRemoteModule({\n       remoteEntry: &#039;https:\/\/brave-plant-03ca65b10.azurestaticapps.net\/remoteEntry.js&#039;,\n       remoteName: &#039;mfe1&#039;,\n       exposedModule: &#039;.\/Module&#039;\n     })\n     .then(m =&gt; m.AppModule) \n },<\/code><\/pre>\n<p>To make exploring this a bit easier, I've provided this Micro Frontend via a Azure Static Web App found at the shown URL.<\/p>\n<p>If you start your shell and load the Micro Frontend, you will see this error.<\/p>\n<p>What can we learn here? Well, when it comes to your leading, stateful framework -- e. g. Angular -- it's a good idea to define it as a singleton. I've written down some details on this and on <a href=\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2021\/07\/https:\/\/www.angulararchitects.io\/aktuelles\/getting-out-of-version-mismatch-hell-with-module-federation\/\">options for dealing with version mismatches<\/a> here.<\/p>\n<p>If you really, really, really want to mix and match different versions of Angular, I've got you covered with <a href=\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2021\/07\/https:\/\/www.angulararchitects.io\/aktuelles\/multi-framework-and-version-micro-frontends-with-module-federation-the-good-the-bad-the-ugly\/\">this article<\/a> and <a href=\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2021\/07\/https:\/\/www.npmjs.com\/package\/@angular-architects\/module-federation-tools\">this library<\/a>. However, you know what they say: Beware of your wishes.<\/p>\n<h2>Bonus: Multiple Bundles<\/h2>\n<p>Let's finish this tour with something, that just looks like an issue but is totally fine. Perhaps you've already seen that sometimes Module Federation generated duplicate bundles with slights different names:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2021\/07\/.\/duplicate-bundles.png\" alt=\"Duplicate Bundles generated by Module Federation\" \/><\/p>\n<p>The reason for this duplication is that Module Federation generates a bundle <strong>per shared library per consumer<\/strong>. The consumer in this sense is the federated project (shell or Micro Frontend) or a shared library. This is done to have a fall back bundle for resolving version conflicts. In general this makes sense while in such a very specific case, it doesn't bring any advantages. <\/p>\n<p>However, if everything is configured in the right way, only one of these duplicates should be loaded at runtime. As long as this is the case, you don't need to worry about duplicates. <\/p>\n<h2>What's next? More on Architecture!<\/h2>\n<p>So far, we've seen that Module Federation is a straightforward 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<h2>Conclusion<\/h2>\n<p>Module Federation is really clever when it comes to auto-detecting details and compensating for version mismatches. However, it can only be as good as the meta data it gets. To avoid getting off the rails, you should remember the following:<\/p>\n<ul>\n<li><strong>requiredVersion<\/strong>: Assign the <code>requiredVersion<\/code> by hand, esp. when working with secondary entrypoints and when having peer dependency warnings. The plugin <code>@angular-architects\/module-federation<\/code> get's you covered with its <code>share<\/code> helper function allowing the option <code>requiredVersion: &#039;auto&#039;<\/code> that takes the version number from your project's <code>package.json<\/code>.<\/li>\n<li><strong>Share dependencies of shared libraries<\/strong> too, esp. if they are also used somewhere else. Also think on secondary entry points.<\/li>\n<li>Make the <strong>shell provide global services<\/strong> the loaded Micro Frontends need, e. g. the <code>HttpClient<\/code> via the <code>HttpClientModule<\/code>.<\/li>\n<li>Never expose the <code>AppModule<\/code> via Module Federation. Prefer to expose lazy Feature modules.  <\/li>\n<li>Use <code>singleton:true<\/code> for Angular and other stateful framework respective libraries.<\/li>\n<li>Don't worry about <strong>duplicated bundles<\/strong> as long as only one of them is loaded at runtime.<\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>The goal of this article is to show typical pitfalls that come when using Module Federation together with Angular and strategies for avoiding them. <\/p>\n","protected":false},"author":9,"featured_media":5185,"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":[18],"tags":[],"class_list":["post-5187","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-uncategorized","post_series-module-federation"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.1.1 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Pitfalls with Module Federation and Angular - 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\/blog\/pitfalls-with-module-federation-and-angular\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Pitfalls with Module Federation and Angular - ANGULARarchitects\" \/>\n<meta property=\"og:description\" content=\"The goal of this article is to show typical pitfalls that come when using Module Federation together with Angular and strategies for avoiding them.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.angulararchitects.io\/en\/blog\/pitfalls-with-module-federation-and-angular\/\" \/>\n<meta property=\"og:site_name\" content=\"ANGULARarchitects\" \/>\n<meta property=\"article:published_time\" content=\"2021-07-23T18:44:12+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2024-01-19T14:21:14+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2021\/07\/pitfall-tout-a.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"1600\" \/>\n\t<meta property=\"og:image:height\" content=\"900\" \/>\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=\"15 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/pitfalls-with-module-federation-and-angular\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/pitfalls-with-module-federation-and-angular\/\"},\"author\":{\"name\":\"Manfred Steyer, GDE\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#\/schema\/person\/15628efa7af4475ffaaeeb26c5112951\"},\"headline\":\"Pitfalls with Module Federation and Angular\",\"datePublished\":\"2021-07-23T18:44:12+00:00\",\"dateModified\":\"2024-01-19T14:21:14+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/pitfalls-with-module-federation-and-angular\/\"},\"wordCount\":2124,\"publisher\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#organization\"},\"image\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/pitfalls-with-module-federation-and-angular\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2021\/07\/pitfall-tout-a.jpg\",\"inLanguage\":\"en-US\"},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/pitfalls-with-module-federation-and-angular\/\",\"url\":\"https:\/\/www.angulararchitects.io\/en\/blog\/pitfalls-with-module-federation-and-angular\/\",\"name\":\"Pitfalls with Module Federation and Angular - ANGULARarchitects\",\"isPartOf\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/pitfalls-with-module-federation-and-angular\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/pitfalls-with-module-federation-and-angular\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2021\/07\/pitfall-tout-a.jpg\",\"datePublished\":\"2021-07-23T18:44:12+00:00\",\"dateModified\":\"2024-01-19T14:21:14+00:00\",\"breadcrumb\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/pitfalls-with-module-federation-and-angular\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.angulararchitects.io\/en\/blog\/pitfalls-with-module-federation-and-angular\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/pitfalls-with-module-federation-and-angular\/#primaryimage\",\"url\":\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2021\/07\/pitfall-tout-a.jpg\",\"contentUrl\":\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2021\/07\/pitfall-tout-a.jpg\",\"width\":1600,\"height\":900},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/pitfalls-with-module-federation-and-angular\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/www.angulararchitects.io\/en\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Pitfalls with Module Federation and Angular\"}]},{\"@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":"Pitfalls with Module Federation and Angular - 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\/blog\/pitfalls-with-module-federation-and-angular\/","og_locale":"en_US","og_type":"article","og_title":"Pitfalls with Module Federation and Angular - ANGULARarchitects","og_description":"The goal of this article is to show typical pitfalls that come when using Module Federation together with Angular and strategies for avoiding them.","og_url":"https:\/\/www.angulararchitects.io\/en\/blog\/pitfalls-with-module-federation-and-angular\/","og_site_name":"ANGULARarchitects","article_published_time":"2021-07-23T18:44:12+00:00","article_modified_time":"2024-01-19T14:21:14+00:00","og_image":[{"width":1600,"height":900,"url":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2021\/07\/pitfall-tout-a.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":"15 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.angulararchitects.io\/en\/blog\/pitfalls-with-module-federation-and-angular\/#article","isPartOf":{"@id":"https:\/\/www.angulararchitects.io\/en\/blog\/pitfalls-with-module-federation-and-angular\/"},"author":{"name":"Manfred Steyer, GDE","@id":"https:\/\/www.angulararchitects.io\/en\/#\/schema\/person\/15628efa7af4475ffaaeeb26c5112951"},"headline":"Pitfalls with Module Federation and Angular","datePublished":"2021-07-23T18:44:12+00:00","dateModified":"2024-01-19T14:21:14+00:00","mainEntityOfPage":{"@id":"https:\/\/www.angulararchitects.io\/en\/blog\/pitfalls-with-module-federation-and-angular\/"},"wordCount":2124,"publisher":{"@id":"https:\/\/www.angulararchitects.io\/en\/#organization"},"image":{"@id":"https:\/\/www.angulararchitects.io\/en\/blog\/pitfalls-with-module-federation-and-angular\/#primaryimage"},"thumbnailUrl":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2021\/07\/pitfall-tout-a.jpg","inLanguage":"en-US"},{"@type":"WebPage","@id":"https:\/\/www.angulararchitects.io\/en\/blog\/pitfalls-with-module-federation-and-angular\/","url":"https:\/\/www.angulararchitects.io\/en\/blog\/pitfalls-with-module-federation-and-angular\/","name":"Pitfalls with Module Federation and Angular - ANGULARarchitects","isPartOf":{"@id":"https:\/\/www.angulararchitects.io\/en\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.angulararchitects.io\/en\/blog\/pitfalls-with-module-federation-and-angular\/#primaryimage"},"image":{"@id":"https:\/\/www.angulararchitects.io\/en\/blog\/pitfalls-with-module-federation-and-angular\/#primaryimage"},"thumbnailUrl":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2021\/07\/pitfall-tout-a.jpg","datePublished":"2021-07-23T18:44:12+00:00","dateModified":"2024-01-19T14:21:14+00:00","breadcrumb":{"@id":"https:\/\/www.angulararchitects.io\/en\/blog\/pitfalls-with-module-federation-and-angular\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.angulararchitects.io\/en\/blog\/pitfalls-with-module-federation-and-angular\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.angulararchitects.io\/en\/blog\/pitfalls-with-module-federation-and-angular\/#primaryimage","url":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2021\/07\/pitfall-tout-a.jpg","contentUrl":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2021\/07\/pitfall-tout-a.jpg","width":1600,"height":900},{"@type":"BreadcrumbList","@id":"https:\/\/www.angulararchitects.io\/en\/blog\/pitfalls-with-module-federation-and-angular\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.angulararchitects.io\/en\/"},{"@type":"ListItem","position":2,"name":"Pitfalls with Module Federation and Angular"}]},{"@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\/5187","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=5187"}],"version-history":[{"count":2,"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/posts\/5187\/revisions"}],"predecessor-version":[{"id":24253,"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/posts\/5187\/revisions\/24253"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/media\/5185"}],"wp:attachment":[{"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/media?parent=5187"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/categories?post=5187"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/tags?post=5187"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}