Angular Micro-Frontends : Module Federation

🏷️ Front-end 📅 14/04/2026 01:50:00 👤 Mezgani said
Angular Micro Frontends Module Federation Architecture Frontend
Angular Micro-Frontends : Module Federation

Construisez une architecture micro-frontends Angular avec Module Federation : découpage, partage de dépendances, routing et stratégie de déploiement.

Concept Module Federation et micro-frontends

Les micro-frontends divisent une Single Page Application en plusieurs applications indépendantes déployables séparément. Chaque équipe possède son domaine métier de bout en bout — du style au déploiement — sans coordination de release avec les autres équipes.

Webpack Module Federation (introduit dans Webpack 5) est le mécanisme technique qui permet à un host de charger à la volée des modules JavaScript compilés par un autre projet (remote), partageant optionnellement des dépendances communes comme Angular ou RxJS.

Quand choisir cette architecture ? Quand plusieurs équipes autonomes livrent des domaines différents à des rythmes décalés, et que partager un seul repo/build Angular crée trop de friction. En dessous de 3 équipes ou d'une app de taille moyenne, un monorepo Nx suffit généralement.

Architecture cible typique :

  • Shell (host) — navigation globale, auth, layout principal
  • Remote catalogue — domaine produits, déployé indépendamment
  • Remote panier — domaine commandes, déployé indépendamment
  • Shared design system — composants UI communs (optionnel)

Mise en place avec @angular-architects/module-federation

Le package @angular-architects/module-federation encapsule la configuration Webpack Module Federation et fournit des schémas Angular CLI pour générer host et remotes automatiquement.

# Dans le projet shell (host)
ng add @angular-architects/module-federation --project shell --port 4200 --type host

# Dans chaque remote
ng add @angular-architects/module-federation --project catalogue --port 4201 --type remote
ng add @angular-architects/module-federation --project panier    --port 4202 --type remote

Ces commandes génèrent un webpack.config.js dans chaque projet et modifient angular.json pour utiliser @angular-architects/module-federation/webpack comme builder custom.

Angular 17+ et esbuild

Module Federation repose sur Webpack. Si votre projet Angular 17+ utilise esbuild (builder par défaut), vous devez revenir à @angular-devkit/build-angular:browser ou utiliser @angular-architects/native-federation, l'alternative basée sur les ES Modules natifs.

Configuration host et remote Webpack

Le remote déclare les modules qu'il expose. Le host déclare les remotes dont il consomme les modules. Voici la configuration minimale pour chacun.

webpack.config.js du remote (catalogue) :

// apps/catalogue/webpack.config.js
const { shareAll, withModuleFederationPlugin } = require('@angular-architects/module-federation/webpack');

module.exports = withModuleFederationPlugin({
    name: 'catalogue',
    exposes: {
        // Alias public → fichier source exposé
        './Module': './apps/catalogue/src/app/catalogue/catalogue.module.ts',
    },
    shared: {
        ...shareAll({ singleton: true, strictVersion: true, requiredVersion: 'auto' }),
    },
});

webpack.config.js du host (shell) :

// apps/shell/webpack.config.js
const { shareAll, withModuleFederationPlugin } = require('@angular-architects/module-federation/webpack');

module.exports = withModuleFederationPlugin({
    remotes: {
        // Clé locale → URL distante (runtime ou statique)
        catalogue: 'http://localhost:4201/remoteEntry.js',
        panier:    'http://localhost:4202/remoteEntry.js',
    },
    shared: {
        ...shareAll({ singleton: true, strictVersion: true, requiredVersion: 'auto' }),
    },
});
remoteEntry.js est le fichier manifeste publié par chaque remote. En production, remplacez localhost:4201 par l'URL CDN/S3 du remote déployé. Vous pouvez aussi charger l'URL dynamiquement depuis une API de configuration.

Partage de dépendances et singleton

Le partage de dépendances évite que chaque remote embarque sa propre copie d'Angular (plusieurs Mo). Avec singleton: true, une seule instance tourne dans la page.

// Shared granulaire (alternative à shareAll)
shared: share({
    '@angular/core': {
        singleton: true,
        strictVersion: true,
        requiredVersion: 'auto',  // lit la version dans package.json
    },
    '@angular/common': { singleton: true, strictVersion: false, requiredVersion: 'auto' },
    '@angular/router': { singleton: true, strictVersion: true,  requiredVersion: 'auto' },
    'rxjs':            { singleton: true, strictVersion: false, requiredVersion: 'auto' },
})

strictVersion et les conflits

Avec strictVersion: true, Webpack lance une erreur si host et remote ont des versions incompatibles. Préférez false pour les libs non critiques (RxJS 7.x est compatible entre remotes Angular 15 et 17). Pour Angular lui-même, gardez true — deux instances Angular dans la même page causent des bugs d'injection difficiles à diagnostiquer.

Routing dynamique et chargement lazy

Le shell charge les remotes en lazy loading via le router Angular. La fonction loadRemoteModule récupère le module exposé par le remote à la demande.

// apps/shell/src/app/app.routes.ts
import { Routes } from '@angular/router';
import { loadRemoteModule } from '@angular-architects/module-federation';

export const APP_ROUTES: Routes = [
    {
        path: '',
        loadComponent: () => import('./home/home.component').then(m => m.HomeComponent),
    },
    {
        path: 'catalogue',
        loadChildren: () =>
            loadRemoteModule({
                type: 'module',
                remoteEntry: 'http://localhost:4201/remoteEntry.js',
                exposedModule: './Module',
            }).then(m => m.CatalogueModule),
    },
    {
        path: 'panier',
        loadChildren: () =>
            loadRemoteModule({
                type: 'module',
                remoteEntry: 'http://localhost:4202/remoteEntry.js',
                exposedModule: './Module',
            }).then(m => m.PanierModule),
    },
];

Pour charger l'URL du remote depuis une API (configuration dynamique) :

// apps/shell/src/app/mf-config.service.ts
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { firstValueFrom } from 'rxjs';

export interface MfManifest {
    [key: string]: string; // ex: { catalogue: 'https://cdn.example.com/catalogue/remoteEntry.js' }
}

@Injectable({ providedIn: 'root' })
export class MfConfigService {
    private manifest: MfManifest = {};

    async load(): Promise {
        this.manifest = await firstValueFrom(
            this.http.get<MfManifest>('/assets/mf-manifest.json'),
        );
    }

    getRemoteUrl(name: string): string {
        return this.manifest[name] ?? '';
    }

    constructor(private http: HttpClient) {}
}

Checklist micro-frontends en production

Avant de déployer votre architecture micro-frontends, validez ces points critiques pour garantir stabilité, performance et maintenabilité.

  • Toutes les dépendances critiques (Angular, RxJS) partagées en singleton: true
  • URLs des remotes externalisées dans un manifeste JSON (pas en dur dans le code)
  • Fallback UI en cas d'échec de chargement d'un remote (try/catch sur loadRemoteModule)
  • CORS configuré sur chaque remote pour autoriser le domaine du shell
  • Headers cache-control adaptés sur remoteEntry.js (courte durée) vs chunks (longue durée)
  • Tests d'intégration e2e couvrant la navigation cross-remote (Cypress)
  • Monitoring distinct par remote en production (erreurs, perf, déploiements)
  • Convention de versioning des interfaces exposées entre équipes (breaking changes)