A microfrontend applique le principe du microservice à la couche présentation : au lieu d'une seule application angulaire monolithique, le frontend est divisé en morceaux indépendants (souvent alignés sur des équipes produit distinctes), chacune avec sa propre construction, déploiement et pipeline cycle de publication, composé avec le runtime ou le build-time en une seule expérience utilisateur. L'avantage Le principal n'est pas technique mais organisationnel : différentes équipes peuvent se libérer de manière autonome sans coordonner un déploiement monolithique partagé. Le compromis tout aussi réel est une complexité supplémentaire de composition, de dépendances partagées et de tests d'intégration - les microfrontends ne sont pas le choix adapté à chaque projet, et devrait être adopté lorsque le coût de la coordination entre les équipes dépasse déjà aujourd'hui le coût de la complexité technique qu’ils introduisent.
Modèles d'intégration : comparaison
| Modèle | Pour | Inconvénients | Quand le préférer |
|---|---|---|---|
| Côté client (shell + télécommandes) | Flexibilité d'exécution maximale, déploiements réellement indépendants | Complexité du partage des dépendances, risque de duplication | Équipes multiples, versions fréquentes indépendant |
| Composition côté serveur | Référencement natif, pas de JS supplémentaire pour la composition | Nécessite une infrastructure de serveur dédiée (SSI/ESI) | Contenu public à fort trafic, critique pour le référencement |
| Edge/reverse proxy | Composition centralisée, cache granulaire par fragment | Le proxy devient un point de défaillance unique | Équipe infra mature, fort besoin de Edge mise en cache |
| Composants Web | Encapsulation DOM native indépendante du framework | Communication entre les composants moins naturelle qu'un framework natif | Équipes avec des piles frontend hétérogènes (non seulement Angulaire) |
| iFrame | Isolement total, zéro conflit CSS/JS | Mauvais UX, communication cross-frame difficile, zéro SEO | Uniquement pour les intégrations non tierces moi |
Pour la plupart des équipes d'entreprise Angular, le modèle côté client avec Module Federation reste le choix par défaut : il équilibre la flexibilité d'exécution et l'intégration naturelle avec le routage et l'injection de dépendances d'Angular, sans l'isolation excessive d'une iframe.
Fédération de modules (Webpack 5) : Shell et Remote
Le shell (hôte) charge dynamiquement les bundles distants au moment de l'exécution, partageant les dépendances les plus courants (Angular, RxJS) en tant que singletons pour éviter de les télécharger plusieurs fois.
// webpack.config.js — SHELL (host)
const { ModuleFederationPlugin } = require('webpack').container;
module.exports = {
plugins: [
new ModuleFederationPlugin({
name: 'shell',
remotes: {
checkout: 'checkout@https://cdn.example.com/checkout/remoteEntry.js',
},
shared: { '@angular/core': { singleton: true, strictVersion: true }, 'rxjs': { singleton: true } },
}),
],
};
// webpack.config.js — REMOTE (checkout)
new ModuleFederationPlugin({
name: 'checkout',
filename: 'remoteEntry.js',
exposes: { './Module': './src/app/checkout/checkout.module.ts' },
shared: { '@angular/core': { singleton: true, strictVersion: true }, 'rxjs': { singleton: true } },
});
// Routing shell — lazy load dinamico del remote
export const routes: Routes = [
{
path: 'checkout',
loadChildren: () => import('checkout/Module').then(m => m.CheckoutModule),
},
];
SPA unique : enregistrement et cycle de vie
// Registrazione di un microfrontend Angular in single-spa
registerApplication({
name: 'checkout',
app: () => import('./checkout.app'),
activeWhen: ['/checkout'],
});
// Lifecycle esposto dal microfrontend
export const { bootstrap, mount, unmount } = singleSpaAngular({
bootstrapFunction: singleSpaProps => bootstrapApplication(CheckoutRootComponent),
template: '',
});
Éléments angulaires / composants Web
// Esporre un componente Angular come Web Component standard
const app = await bootstrapApplication(ProductCardComponent);
const element = createCustomElement(ProductCardComponent, { injector: app.injector });
customElements.define('product-card', element);
Le consommateur, bien que non basé sur Angular, utilise le composant comme n'importe quelle balise HTML native :
— c'est ce qui rend le Web
Les composants sont la bonne option lorsque le shell ou d’autres microfrontends ne sont pas angulaires.
Alternatives plus légères : importer une carte avec des modules ES dynamiques (pas d'outil de build
dédié à la fédération, seul le natif import() pointait vers des URL versionnées) pour les équipes qui
je veux éviter la complexité de configuration de la fédération de modules et le modèle Nx
monorepo lorsque les microfrontends partagent de toute façon le même référentiel mais veulent
maintenir des pipelines de construction indépendants via le graphique affecté.
Système de partage et de conception de dépendances
Angular et RxJS doivent toujours être partagés en tant que singleton : true dans la fédération de modules : sans
ceci, chaque distant télécharge sa propre copie du framework, gonflant le bundle et le risquant
modifier l'incompatibilité de détection entre le shell et la télécommande chargée ensemble.
// package.json della libreria di design system condivisa — peerDependencies, non dependencies
{
"name": "@azienda/mfe-ui",
"peerDependencies": { "@angular/core": "^18.0.0", "rxjs": "^7.0.0" }
}
Publier les composants principaux et les jetons de conception en tant que bibliothèque versionnée distincte (non dupliquée dans chaque microfrontend), documenté dans Storybook — exactement le même modèle qu'un système de conception centralisé, à la seule différence qu'ici les consommateurs sont des microfrontends indépendants au lieu de applications distinctes.
Routage, état et communication entre les microfrontends
Le routing global (quel microfrontend afficher) reste sous la responsabilité du shell ; chaque distant gère son propre routage local (les sous-routes internes) dans un complètement indépendant — le shell n'a pas besoin de connaître la structure de routage interne d'un distant, juste son chemin d'entrée.
// Event bus condiviso con RxJS — comunicazione shell/remote senza accoppiamento diretto
export class MfeEventBus {
private readonly subject = new Subject<{ type: string; payload: unknown }>();
emit(type: string, payload: unknown) { this.subject.next({ type, payload }); }
on(type: string) { return this.subject.pipe(filter(e => e.type === type), map(e => e.payload)); }
}
Pour le statut partagé, trois options avec des compromis différents : l'URL (la plus simple et robuste, mais limité à un état sérialisable), un event bus comme ci-dessus (découplé mais sans son propre état persistant), ou un store partagé publié sous forme de bibliothèque singleton (plus puissant mais introduit un couplage implicite entre les microfrontends qui doivent s'accorder sur un formulaire étatique commun). Le contrat entre le shell et le distant doit toujours être explicite et tapé — props événements entrants et sortants, jamais d'accès direct et non documenté à l'état interne d'un autre télécommande.
CI/CD, tests et déploiement
Chaque microfrontend possède son propre pipeline indépendant : build, tests unitaires, Storybook et publication del
remoteEntry.js empreintes digitales sur CDN, sans dépendre du déploiement d'autres microfrontends.
# GitHub Actions — sintesi pipeline per un singolo microfrontend
jobs:
build-and-deploy:
steps:
- run: npm ci
- run: npm run build -- --configuration production
- run: npm test -- --watch=false
- run: npx cypress run
- run: aws s3 cp dist/checkout s3://cdn.example.com/checkout/$GITHUB_SHA --recursive
- run: node scripts/update-manifest.js checkout $GITHUB_SHA
// scripts/update-manifest.js — aggiorna il manifest che la shell legge per risolvere i remoteEntry
const manifest = JSON.parse(fs.readFileSync('manifest.json'));
manifest[process.argv[2]] = `https://cdn.example.com/${process.argv[2]}/${process.argv[3]}/remoteEntry.js`;
fs.writeFileSync('manifest.json', JSON.stringify(manifest, null, 2));
Pour l'intégration entre shell et distant, deux niveaux de tests : contract test qui vérifier que la télécommande expose l'interface attendue (accessoires/événements) sans déployer l'intégralité du shell, e E2E contre un environnement de mise en scène avec le vrai shell et les vraies télécommandes, réservées aux flux critiques entre microfrontends - simulations de télécommandes dans les tests unitaires shell, ne vous contentez pas de vous fier à cela pour valider une réelle intégration.
Performances et optimisation
<!-- Prefetch del remoteEntry del prossimo microfrontend probabile, senza bloccare il rendering corrente -->
<link rel="prefetch" href="https://cdn.example.com/checkout/remoteEntry.js" as="script" />
- Chargement paresseux de chaque télécommande uniquement lorsque l'itinéraire correspondant est visité, jamais dans le bundle shell initial.
- Fingerprinting de remoteEntry (hachage dans le chemin, pas dans le nom de fichier pour des raisons de compatibilité avec Module Federation) pour un cache long sans invalidations accidentelles.
- Analyseur de bundles pour chaque télécommande indépendamment, pas seulement sur le shell agrégé, pour détecter les duplications de dépendances qui ne sont pas partagées correctement.
- TTI/LCP mesuré par microfrontend unique, pas seulement sur la page globale : une télécommande lente dégrade la perception de l'ensemble de la coque même si le reste est rapide.
Sécurité et opérations
# CSP che permette script solo dai domini CDN dei microfrontend fidati
Content-Security-Policy: script-src 'self' https://cdn.example.com; connect-src 'self' https://cdn.example.com;
HTTPS requis sur chaque CDN desservant une entrée distante et CORS explicitement configuré pour permettre au shell de charger des scripts multi-origines à partir de domaines microfrontend. Gérez toujours le échec du chargement d'une télécommande avec un composant de secours explicite, jamais une page blanche : une microinterface secondaire qui ne répond pas ne devrait pas empêcher l’utilisation du reste de l’application.
// Fallback route nella shell se un remote non è raggiungibile
loadChildren: () => import('checkout/Module')
.then(m => m.CheckoutModule)
.catch(() => import('./fallback/checkout-unavailable.module').then(m => m.CheckoutUnavailableModule)),
La surveillance doit être définie pour une seule microfrontend, et pas seulement pour un agrégat : taux d'erreur, déploiement Fréquence de chargement et latence de RemoteEntry pour chaque équipe, afin qu'un problème soit identifié immédiatement dans le microfrontend responsable au lieu de génériquement "dans le shell".
Évolutivité et restauration
Chaque déploiement d'un distant est versionné (chemin avec hash/SHA du commit sur CDN) ; la restauration consiste à repointer le manifeste vers la version précédente, sans avoir besoin d'un nouveau déploiement ou reconstruction - l'opération la plus rapide possible en cas de régression.
# Rollback: ripunta il manifest alla versione precedente del remote
node scripts/update-manifest.js checkout $PREVIOUS_SHA
Pour des déploiements fluides, utilisez feature flag au niveau du shell (quelle version du manifeste servir à quel pourcentage d'utilisateurs) au lieu d'un canari au niveau de l'infrastructure plus complexe à orchestrer pour des fragments d’interface utilisateur individuels.
Étude de cas 1 : Marketplace avec 4 équipes produit
Une marketplace composée de 4 équipes indépendantes (recherche, produit, paiement, compte) a adopté Module Fédération avec shell centralisé. Effort de configuration initial : environ 120 heures-personnes pour le shell, le manifeste et Pipeline CI/CD partagé. Après 5 mois : la fréquence de déploiement est passée de 1/semaine (monolithe) à 12/semaine regroupés sur les 4 équipes, temps de restauration moyen de 25 minutes (reconstruction complète) à 40 minutes secondes (mise à jour du manifeste), 0 conflit de fusion entre équipes sur le frontend.
Étude de cas 2 : Plateforme SaaS en expansion à l'international
Une plateforme SaaS avec des équipes réparties sur 3 fuseaux horaires a adopté des composants Web pour permettre une équipe avec la stack React à intégrer dans le shell Angular existant sans rien réécrire. Effort : ~ 80 heures-personnes pour le premier composant Web distant et la bibliothèque de conception de jetons partagée. Résultat : délai de commercialisation de la première fonctionnalité inter-équipes réduit de 6 à 3 semaines, pas de dépendances forcées à partir d'une seule pile frontale pour l'intégration de nouvelles équipes.
Liste de contrôle opérationnel avant le démarrage
- Délimitation claire du domaine d'activité entre les microfrontends, aligné sur l'équipe, non arbitraire.
- Système de conception partagée publié avant la première télécommande, pas après.
- Contrat Shell-Remote (accessoires/événements) explicitement documenté et versionné.
- Pipeline CI/CD indépendant déjà prêt pour au moins la première télécommande avant d'en ajouter une seconde.
Forfait 30/60/90 jours
Jours 1-30
- Shell et premier distant en production avec Module Federation — KPI : taux de réussite de build 100 %, déploiement indépendant vérifié.
Jours 31-60
- Deuxième et troisième télécommande intégrée, surveillance active par microfrontend — KPI : taux d'erreur suivi séparément pour chaque télécommande.
Jours 61-90
- Rollback testé de bout en bout, indicateurs de fonctionnalité pour les déploiements progressifs actifs — KPI : temps de restauration mesuré en moins d'une minute.
FAQ
La fédération de modules duplique-t-elle toujours Angular s'elle n'est pas correctement configurée ?
Oui, sans singleton : true dans la section partagée, chaque télécommande télécharge sa propre copie du framework, gonflant le bundle et risquant une incompatibilité.
Comment gérer le routage lorsqu'une télécommande n'est pas accessible ?
Avec un repli explicite dans la chaîne de chargement paresseux, ne jamais laisser l'erreur d'importation se propager comme une page blanche.
Avez-vous vraiment besoin de Nx pour créer des microfrontends avec Angular ?
Non, cela est utile pour orchestrer plusieurs projets dans le même référentiel, mais Module Federation fonctionne également avec des référentiels complètement séparés.
Combien de microfrontends sont « de trop » ?
Il n'y a pas de numéro fixe ; le signal d’alarme survient lorsque le coût de la coordination entre les contrats à distance dépasse les avantages des déploiements indépendants.
Comment tester les intégrations entre le shell et le distant ?
Avec des tests contractuels sur des télécommandes individuelles plus E2E sur un environnement de test avec de vrais shells et télécommandes pour les flux critiques.
Les microfrontends ralentissent-ils les performances par rapport à un monolithe ?
Peut, s'il n'est pas géré avec un chargement paresseux et une prélecture corrects ; bien configuré, l’impact est marginal par rapport aux avantages organisationnels.
Comment partager des jetons de conception entre différentes microfrontends ?
Avec une bibliothèque publiée séparément, versionnée et consommée en tant que dépendance homologue par chaque télécommande.
Fédération de composants Web ou de modules ?
Fédération de modules lorsque tous les microfrontends sont angulaires ; Composants Web lorsque la pile est hétérogène ou qu'une isolation indépendante du framework est nécessaire.
Comment puis-je restaurer rapidement une seule microfrontend ?
Repointage du manifeste vers la version précédente de remoteEntry, sans avoir besoin d'un nouveau déploiement.
Avez-vous besoin d'un shell dédié ou peut-il également s'agir d'une application ?
Un shell dédié, minimal et stable est préférable, pour réduire le risque que son redéploiement impacte toutes les télécommandes en même temps.
Comment éviter la duplication CSS entre les microfrontends ?
Avec un système de conception partagé comme seule source de styles de base et une encapsulation (Shadow DOM ou convention de dénomination rigoureuse) pour les styles spécifiques de chaque télécommande.
Les microfrontends ont-ils un sens même pour une seule équipe ?
Rarement : le principal avantage est organisationnel. Avec une seule équipe, le coût de la composition de l'exécution dépasse généralement les avantages.
Erreurs courantes à éviter
- Angular non partagé en tant que singleton : provoque une duplication du framework et un bug de détection de changement entre le shell et le distant.
- Aucune solution de secours UX pour les télécommandes inaccessibles : Transformez un problème isolé en une page blanche pour l'ensemble de l'application.
- Contrat shell-remote non documenté : chaque équipe devine l'interface, provoquant des régressions silencieuses à chaque déploiement.
- Limites de domaine arbitraires entre les microfrontends : Générez une communication croisée à distance excessive au lieu de réduire le couplage.
- Système de conception en double au lieu de partagé : produit une incohérence visuelle entre les microfrontends en quelques semaines.
- Aucun test contractuel entre le shell et le distant : les régressions d'intégration ne sont découvertes qu'en production.
- Déployer des télécommandes non versionnées : rend la restauration aussi lente et risquée qu'une reconstruction complète.
- Surveillance globale uniquement sur le shell : vous empêche d'identifier rapidement quelle microfrontend est à l'origine d'un problème.
- iFrame utilisé pour la composition interne entre des équipes de confiance : n'introduit aucune complexité de communication et de référencement sans le besoin réel d'un isolement total.
- Pas de prélecture des télécommandes probables : chaque navigation vers une nouvelle microfrontend paie le coût total du chargement à froid.
Comment vérifier
- Construisez chaque télécommande isolée :
npx webpack --config webpack.config.jset recherchez les erreurs. - Tests E2E sur obus réels + télécommandes en staging :
npx cypress run. - Vérification du manifeste : vérifiez que chaque entrée pointe vers un
remoteEntry.jsréellement accessible (curl -Isur chaque URL). - Contrôlez la taille du paquet par télécommande individuelle avec un analyseur de paquet, pas seulement sur la coque globale.
- Test de secours : désactivez temporairement une télécommande et vérifiez que le shell affiche le composant de secours au lieu d'une erreur.
- Surveillance active : vérifiez que le taux d'erreur, la fréquence de déploiement et la latence sont suivis séparément pour chaque microfrontend.
Conclusion
Les microfrontends avec Angular résolvent un problème organisationnel, pas principalement technique : le nécessité pour plusieurs équipes de publier de manière autonome sans coordonner un monolithe partagé. Module Fédération avec des dépendances partagées telles que des singletons, un système de conception centralisé, des contrats explicite entre le shell et le distant, et une restauration basée sur un manifeste versionné sont les éléments qui distinguer une architecture microfrontend véritablement durable d’une complexité accrue sans véritable bénéfice.
Voulez-vous un dépôt squelette pour démarrer ou une évaluation de votre architecture frontend existant ? Demander un audit technique : en quelques heures d'analyse il est possible d'identifier le Corrigez les limites des domaines et les principaux risques liés à l'adoption d'un microfrontend pour votre équipe.