Demonstration Application

The demo project featured here is a compact shell capable of pulling in remotes from both Module Federation and Native Federation. Shared dependencies are coordinated across the two approaches:

Getting Started

Begin by scaffolding a shell with Native Federation, then incorporate the Module Federation Runtime to handle remotes from that ecosystem. The runtime ships inside the @module-federation/enhanced package:

npm i @module-federation/enhanced

For every Native Federation project that installs or employs the Module Federation Runtime, make sure it is excluded from shared dependencies in the federation.config.js:

module.exports = withNativeFederation({
  ...
  skip: [
    ...
    /^@module-federation/,  
        // Use RegExp to skip ALL entry points!
  ]
});

Shell Bootstrap

During shell startup, both runtimes need to be initialized side by side:

// main.ts
import { initFederation as initNativeFederation } from '@angular-architects/native-federation';
import { init as initModuleFederation } from '@module-federation/enhanced/runtime';
import { getShared } from './app/shared/federatio-helpers';

(async () => {
  // Step 1: Initialize Native Federation
  await initNativeFederation('federation.manifest.json');

  // Step 2: Get metadata about libs shared via Native Federation
  const shared = getShared();

  // Step 3: Initialize Module Federation
  //  Remarks: Consider loading this MF config via the fetch API
  initModuleFederation({
    name: 'shell',
    remotes: [
      {
        name: 'modfed-mf',
        entry: 'http://localhost:4201/remoteEntry.js',
        type: 'esm',
      },
      {
        name: 'react',
        entry:
          'https://witty-wave-0a695f710.azurestaticapps.net/remoteEntry.js',
      },
      {
        name: 'angular2',
        entry: 'https://gray-pond-030798810.azurestaticapps.net/remoteEntry.js',
      },
      {
        name: 'angular3',
        entry:
          'https://gray-river-0b8c23a10.azurestaticapps.net/remoteEntry.js',
      }
    ],
    // Step 3a: Delegate shared libs from Native Federation
    shared,
  })
  .initializeSharing();

  // Step 4: Delegate to file bootstrapping the SPA
  await import('./bootstrap');
})();

The getShared utility (see the accompanying source) extracts metadata about libraries shared through Native Federation. This same metadata is then passed along into the Module Federation setup, creating a bridge between the two worlds.

Fetching the Micro Frontends

When it comes time to load Micro Frontends, pick the right loader: use the helper from @module-federation/enhanced/runtime for Module Federation-based components, and the one from @angular-architects/native-federation (or @softarc/native-federation for non-Angular projects) for Native Federation-based ones:

...
import { loadRemote as loadModuleRemote } from '@module-federation/enhanced/runtime';
import { loadRemoteModule as loadNativeRemote } from '@angular-architects/native-federation';

export const routes: Routes = [
    {
        path: '',
        pathMatch: 'full',
        component: HomeComponent,
    },

    // MF-based Micro Frontend
    {
        path: 'modfed-mf',
        loadComponent: () => loadModuleRemote<any>('modfed-mf/Component').then(m => m.AppComponent)
    },

    // NF-based Micro Frontend
    {
        path: 'natfed-mf',
        loadComponent: () => loadNativeRemote('natfed-mf', './Component').then(m => m.AppComponent)
    },

    ...
]

To accommodate multiple versions of additional frameworks, the example also includes a Wrapper component that loads Web Components through Federation. That technique is covered in detail elsewhere on this blog.

Looking Ahead

Although the demo illustrates how these two systems can coexist, a production setup would likely benefit from more abstraction. For instance, you could fetch the Module Federation Runtime configuration from a file, mirroring how the federation.manifest.json works for Native Federation.

Another route is to retrieve metadata for both worlds from a service. Then a custom helper could distribute the relevant entries to Module and Native Federation at bootstrap time. That same metadata could also power a loader function capable of deciding, per remote, which federation flavor to invoke.

Deeper Dive: Angular Architecture Workshop (online, interactive, advanced)

Take your skills to the next level and master enterprise-scale, maintainable Angular applications through our Angular Architecture workshop!Combining Native Federation and Module Federation — figure 1

All Details (English Workshop) | All Details (German Workshop)