Ce repository sert de reference pour mettre en place une architecture micro-frontend Angular basee sur @angular-architects/module-federation, avec configuration runtime externe, menu centralise et deploiement Azure.
L'objectif de ce document est d'expliquer comment reproduire cette approche dans un autre projet existant, avec les contraintes suivantes :
- un shell de type
foundation - plusieurs remotes deja existants mais non prepares pour Module Federation
- une route
/qui charge elle aussi un remote dedie - une logique de menu provenant d'un repo
ui-kit - un repo
cloud-config-repoqui porte le manifest et le menu runtime - un
state-bridgeoptionnel pour partager des contrats et de l'etat - un hebergement Azure pour le shell, les remotes et la configuration
Le montage recommande est le suivant :
foundation: host Angular qui charge les remotes et gere la navigation globalehome-remote: nouveau remote charge sur la route/remote-a,remote-b,remote-c: applications existantes rendues compatibles Module Federationui-kit: repo partage qui expose les composants de menu, les types et les helpers UI communscloud-config-repo: repo de fichiers statiques JSON qui contientmenu.jsonetmf-manifest.jsonstate-bridge: package optionnel pour contrats partages, evenements et etat transverse
Le cycle de resolution est toujours le meme :
foundationlit unruntime-config.jsonlocal- ce fichier pointe vers
cloud-config-repo foundationtelechargemenu.jsonetmf-manifest.json- le menu runtime construit la navigation visible
- le manifest runtime indique a Module Federation ou trouver les remotes
- a chaque navigation,
foundationrelit la configuration pour detecter nouveaux remotes, changements d'URL et changements de version
Pour reproduire ce modele dans un autre contexte, la separation la plus simple est :
- un repo pour le shell
- un repo par remote
- un repo
ui-kit - un repo
cloud-config-repo - un repo
state-bridgesi vous avez besoin de contrats ou d'etat partages
Cette separation est importante car :
- elle permet de deployer un remote sans rebuild du shell
- elle permet de deployer une nouvelle configuration sans rebuild du shell
- elle garde le menu et le manifest hors du bundle Angular du host
Le shell doit rester une application Angular classique, mais avec un bootstrap runtime.
Le shell doit lui aussi etre configure pour Module Federation. Il ne suffit pas de configurer uniquement les remotes.
Installation minimale cote foundation :
- installer
@angular-architects/module-federation - installer
@angular-builders/custom-webpack - configurer
angular.jsonpour utiliser les builders custom webpack - ajouter un
webpack.config.js - declarer les dependances partagees Angular et les packages internes communs
- utiliser
setManifest(...)etloadRemoteModule(...)dans le shell
Exemple minimal de webpack.config.js cote host :
const { share, withModuleFederationPlugin } = require('@angular-architects/module-federation/webpack');
const federationConfig = withModuleFederationPlugin({
name: 'foundation',
library: {
type: 'var',
name: 'foundation'
},
shared: share({
'@angular/core': { singleton: true, strictVersion: false, requiredVersion: 'auto' },
'@angular/common': { singleton: true, strictVersion: false, requiredVersion: 'auto' },
'@angular/common/http': { singleton: true, strictVersion: false, requiredVersion: 'auto' },
'@angular/router': { singleton: true, strictVersion: false, requiredVersion: 'auto' },
rxjs: { singleton: true, strictVersion: false, requiredVersion: 'auto' }
})
});
module.exports = {
...federationConfig,
output: {
...federationConfig.output,
publicPath: '/'
}
};Exemple de points a verifier dans angular.json cote shell :
- builder
@angular-builders/custom-webpack:browserpour le build - builder
@angular-builders/custom-webpack:dev-serverpour le serve customWebpackConfig.pathqui pointe verswebpack.config.js
Principes a reprendre depuis ce PoC :
- charger la configuration avant
bootstrapApplication - injecter la configuration runtime dans Angular
- construire les routes a partir du menu runtime
- appeler
setManifest(...)avec le manifest telecharge - relire la configuration a chaque navigation
- afficher une bannere de refresh si la
versiond'un remote change dans le manifest
Le shell doit posseder au minimum :
- un
public/runtime-config.json - un loader de config runtime
- un service shell qui gere le refresh de configuration
- une fonction
createAppRoutes(...) - une UI shell capable d'afficher le menu et la bannere de refresh
Dans ce PoC, / est encore une page locale du shell. Dans votre projet cible, il faut faire evoluer cette logique pour que / charge un remote dedie, par exemple homeRemote.
La convention recommandee est :
- creer un repo
home-remote - exposer ses routes via
./Routes - declarer une entree de menu avec
route: "/"etremoteName: "homeRemote" - declarer
homeRemotedansmf-manifest.json - adapter
createAppRoutes(...)dansfoundationpour autoriser un remote sur la route vide
Exemple de menu cible :
[
{
"id": "home",
"label": "Accueil",
"route": "/",
"description": "Page d'accueil du domaine",
"tag": "Remote",
"remoteName": "homeRemote",
"exposedModule": "./Routes"
},
{
"id": "inventory",
"label": "Inventory",
"route": "/inventory",
"description": "Stock et approvisionnement",
"tag": "Remote",
"remoteName": "inventoryRemote",
"exposedModule": "./Routes"
}
]Exemple de manifest cible :
{
"homeRemote": {
"type": "module",
"remoteEntry": "https://example-home-remote.azurestaticapps.net/remoteEntry.js?v=1.0.0",
"version": "1.0.0"
},
"inventoryRemote": {
"type": "module",
"remoteEntry": "https://example-inventory.azurestaticapps.net/remoteEntry.js?v=2.3.1",
"version": "2.3.1"
}
}Point d'attention :
- dans le shell, la route Angular pour
/correspond apath: "" - votre fabrique de routes doit donc accepter qu'un item de menu
"/"soit transforme en remote racine au lieu d'etre reserve a une page locale
Pour chaque SPA existante qui doit devenir un remote :
- installer
@angular-architects/module-federation - installer
@angular-builders/custom-webpacksi ce n'est pas deja le cas - configurer le builder Angular pour utiliser le webpack custom
- creer un
webpack.config.jsavecwithModuleFederationPlugin(...) - exposer
./Routes - verifier que le remote produit bien
remoteEntry.js
Exemple minimal de webpack.config.js :
const { share, withModuleFederationPlugin } = require('@angular-architects/module-federation/webpack');
module.exports = withModuleFederationPlugin({
name: 'inventoryRemote',
filename: 'remoteEntry.js',
exposes: {
'./Routes': './src/app/remote-entry/entry.routes.ts'
},
shared: share({
'@angular/core': { singleton: true, strictVersion: false, requiredVersion: 'auto' },
'@angular/common': { singleton: true, strictVersion: false, requiredVersion: 'auto' },
'@angular/router': { singleton: true, strictVersion: false, requiredVersion: 'auto' },
rxjs: { singleton: true, strictVersion: false, requiredVersion: 'auto' }
})
});Exemple minimal de routes exposees :
import { Routes } from '@angular/router';
export const remoteRoutes: Routes = [
{
path: '',
loadComponent: () => import('../inventory-shell.component').then((m) => m.InventoryShellComponent)
}
];Pour un remote deja existant, l'idee n'est pas de tout refaire. Il faut surtout :
- isoler son routeur interne sous une route racine
path: '' - creer un fichier
remote-entry/entry.routes.ts - verifier que les composants principaux peuvent etre charges via
loadComponentouloadChildren - retirer toute hypothese selon laquelle l'application est toujours servie seule sur
/
Checklist de migration pour un remote existant :
- conserver les pages et composants metier tels quels
- ajouter une coquille
RemoteShellComponentsi besoin - exposer les routes a travers
./Routes - verifier les chemins relatifs et les assets
- verifier les redirects internes
- verifier les
routerLinkinternes sous le prefixe du remote
Si le remote doit aussi continuer a tourner en autonome, gardez :
- un
AppComponentpour le mode standalone - un
entry.routes.tspour le mode federated
Dans votre projet cible, la logique de menu ne doit pas vivre en dur dans foundation.
Le repo ui-kit devrait idealement porter :
- les types du menu
- les helpers de validation ou normalisation
- le composant visuel de navigation si vous voulez mutualiser le rendu
- les tokens design communs
Le flux recommande est :
cloud-config-repo/menu.jsonreste la source de verite runtimefoundationtelecharge ce JSONfoundationle valide ou le normalise via un helper venant deui-kitfoundationl'affiche avec son layout shell
Le ui-kit n'est donc pas la source runtime des donnees, mais la source partagee de presentation et de typage.
cloud-config-repo doit rester un repo de fichiers statiques, sans logique backend metier.
Structure minimale :
public/config/menu.jsonpublic/config/mf-manifest.json
foundation/public/runtime-config.json doit simplement contenir :
{
"configBaseUrl": "https://votre-config.azureedge.net/config"
}Bonnes pratiques pour mf-manifest.json :
- toujours declarer
type - toujours declarer
remoteEntry - toujours declarer
version - si possible, versionner ou cache-buster aussi
remoteEntry
La valeur version sert a detecter un changement a la navigation. L'URL versionnee du remoteEntry aide a forcer le navigateur a recuperer les nouveaux fichiers apres reload.
Les mecanismes de foundation de ce PoC doivent etre repris dans le projet cible :
- chargement de
runtime-config.json - chargement de
menu.jsonetmf-manifest.json - injection de cette config dans le shell
- construction dynamique des routes
setManifest(...)- refresh de configuration a chaque navigation
- bannere de refresh si
versionchange
Dans le projet cible, il est recommande de conserver la meme separation de responsabilites :
- un loader pour la configuration runtime
- un service shell pour le refresh et les comparaisons
- un composant shell pour le layout et la bannere
- une fabrique de routes pour transformer le menu runtime en routes Angular
Une fois le systeme en place, ajouter un nouveau remote doit se limiter a :
- deployer le nouveau remote
- ajouter son entree dans
mf-manifest.json - ajouter son entree dans
menu.json - naviguer dans
foundation
Le shell doit alors :
- relire la configuration
- afficher le nouveau lien
- charger le nouveau remote sans rebuild du host
Le state-bridge n'est pas obligatoire pour faire fonctionner Module Federation.
Il devient utile si vous avez besoin de :
- contrats TypeScript partages
- etat commun entre shell et remotes
- evenements ou mutations transverses
- mocks ou adaptateurs communs
Si vous l'ajoutez, traitez-le comme un package partage, pas comme un remote. Il doit etre :
- versionne
- buildable
- reference comme dependance locale ou package interne
- partage en singleton dans la config Module Federation
Si vous n'en avez pas besoin tout de suite, vous pouvez commencer sans lui puis l'ajouter plus tard.
En production Azure, le montage recommande est :
foundationheberge sur Azure Static Web Apps ou Blob Storage static website- chaque remote heberge sur son propre endpoint statique
cloud-config-repoheberge comme fichiers statiquesui-kitetstate-bridgeintegres au build des applications qui les consomment
Exemples d'hebergement possibles :
- Azure Static Web Apps
- Azure Blob Storage static website
- Azure CDN ou Front Door devant les assets
Points d'attention Azure :
- activer CORS si
foundationetcloud-config-reposont sur des origines differentes - definir des regles de cache claires pour
mf-manifest.jsonetmenu.json - preferer
cache-control: no-storeou TTL court pour les JSON de configuration - prevoir un versioning ou hash pour les
remoteEntry.jset chunks
Pour deployer une nouvelle version d'un remote :
- incrementer sa version
- construire et publier le remote
- mettre a jour
mf-manifest.jsonavec : la nouvelleversionla nouvelle URL ou query string deremoteEntry - redeployer
cloud-config-repo
Pour ajouter un nouveau remote :
- rendre le repo compatible MF
- publier le remote
- l'ajouter au manifest
- l'ajouter au menu
- verifier la navigation dans le shell
- creer ou adapter
foundation - creer un
home-remotepour/ - rendre chaque SPA compatible MF
- sortir le menu runtime dans
cloud-config-repo - conserver les types et composants communs dans
ui-kit - ajouter
state-bridgeseulement si necessaire - heberger shell, remotes et config comme assets statiques sur Azure
- comparer les
versionde manifest a chaque navigation - afficher une bannere de refresh si une version change
- commencez par un shell + un seul remote avant de brancher tous les domaines
- rendez d'abord un remote existant compatible MF, puis industrialisez la recette
- gardez
cloud-config-repotres simple - ne faites pas porter au shell des routes compilees en dur si vous voulez ajouter des remotes sans rebuild
- testez explicitement les rollbacks en changeant la valeur
versiondans le manifest
Ce repository fournit deja les briques principales a reprendre :
- un shell runtime avec refresh de configuration
- un mecanisme de detection de changement de version
- des remotes Angular exposes via
./Routes - un repo de configuration externe
- un package de menu partage
- un
state-bridgeoptionnel pour les scenarios d'etat transverse
Le principal ecart a implementer dans votre projet cible est la mise en remote de la route /.