angularmicro-frontendangular14webpack-module-federationangular-module-federation

How to run shared libs/services as singletons with micro frontend in Angular (Module Federation)


I have a Monorepo Angular Workspace and I'm using @angular-architects/module-federation 14.13.14 and have been following the documentation to create a POC.

I've been trying to run a single instance of a local shared library across my micro frontends but no cigar.

Here's the structure of my workspace:

Workspace
|- Projects
   |- shell
   |- app1
   |- lib1
      |- service
         |- name: BehaviorSubject<string>

I've used dependency injection to subscribe to name on both shell and app1. I've created a method on shell to pass a new value to name (this.lib1Service.name.next('some other thing'). But this doesn't reflect on app1, only on shell.

I've added path mapping to tsconfig.json as instructed by the author:

 "paths": {
   "lib1": [
     "projects/lib1/src/public-api.ts"
   ]
 },

According to the documentation, this would be enough in the new version of the plugin:

New streamlined configuration in version 14+

Beginning with version 14, we use a more steamlined configuration, when using the above mentioned --type switch with one of the following options: remote, host, dynamic-host. This new configuration automatically shares all local libraries. Hence, you don't need to do a thing.

I've also tried to add the library to the sharedMappings array on both shell and app1 webpack.config.js files:

sharedMappings: ['lib1'],

This is my shell's webpack.config.js:

const { shareAll, share, withModuleFederationPlugin } = require('@angular-architects/module-federation/webpack');

module.exports = withModuleFederationPlugin({
  shared: {
    ...shareAll({ singleton: true, strictVersion: true, requiredVersion: 'auto' }),
  },

 // sharedMappings: ['lib1'], // tried uncommenting this but it doesn't work
 },
});

EDIT: I've also tried manually sharing each library as singletons and include the library amongst them in my shell's webpack config file:

shared: share({
    "@angular/core": {
      singleton: true,
      strictVersion: true,
      requiredVersion: "auto",
    },
    "@angular/common": {
      singleton: true,
      strictVersion: true,
      requiredVersion: "auto",
    },
    "@angular/common/http": {
      singleton: true,
      strictVersion: true,
      requiredVersion: "auto",
    },
    "@angular/router": {
      singleton: true,
      strictVersion: true,
      requiredVersion: "auto",
    },
    "lib1": { singleton: true, import: "../lib1/src/public-api.ts" },
    "rxjs": {
      singleton: true,
      requiredVersion: "auto"
    }
  })

What am I doing wrong? Please help :)


Solution

  • FOR ANYONE WHO STUMBLES UPON THIS THREAD: I've found the solution. The import in your components that are consuming the service should match the name of your path mapping on tsconfig.json.

    For example, I've declared my shared lib like this on tsconfig.json:

        "paths": {
          "lib1": [
            "./projects/lib1/src/public-api.ts"
          ]
        },
    

    So, in my micro frontends, I should import the service like this:
    import { Lib1Service } from 'lib1';
    instead of
    import { Lib1Service } from 'projects/lib1/src/public-api';

    And that's it. Manfred Steyer was right. Once you declared the shared lib in your tsconfig.json, you won't need to do anything else (if you're using the shareAll approach in your webpack.config.js, that is).