Micro-frontends : Quand et comment les utiliser
Les micro-frontends sont une évolution naturelle des microservices appliqués au frontend. Mais comme toute architecture, ce n’est pas une solution universelle.
Pourquoi les micro-frontends ?
Après avoir travaillé sur plusieurs projets à grande échelle, voici les vraies raisons d’adopter cette architecture :
1. Scalabilité des équipes
Avec 50+ développeurs sur un monolithe, les conflits de merge deviennent insoutenables. Les micro-frontends permettent à chaque équipe d’évoluer indépendamment.
2. Déploiement indépendant
Chaque équipe peut déployer sans attendre les autres. Les cycles de release passent de semaines à jours, voire heures.
3. Hétérogénéité technologique
Besoin de migrer de Angular vers React ? Faites-le progressivement, module par module, sans réécriture totale.
Les approches
1. Build-time integration (la plus simple)
// Chaque micro-frontend est un package npm
import { UserModule } from '@company/users-mfe';
import { ProductsModule } from '@company/products-mfe';
Avantages : Simple, performant Inconvénients : Nécessite rebuild pour chaque changement
2. Run-time integration via Module Federation
Webpack 5 a révolutionné l’approche avec Module Federation :
// webpack.config.js
new ModuleFederationPlugin({
name: 'users',
filename: 'remoteEntry.js',
exposes: {
'./UserApp': './src/UserApp'
},
shared: ['react', 'react-dom']
})
Avantages : Déploiement indépendant, pas de rebuild Inconvénients : Complexité accrue, débogage plus difficile
3. iframes (oui, vraiment)
Ne les écartez pas trop vite. Pour certains use cases (isolation stricte, legacy), c’est une solution viable.
Les pièges à éviter
1. Duplication des dépendances
Sans partage correct, vous vous retrouvez avec 5 instances de React. Utilisez Module Federation ou des shared dependencies.
2. Communication inter-MFE anarchique
Établissez un bus d’événements clair :
// Event bus centralisé
class EventBus {
private subscribers = new Map<string, Set<Function>>();
subscribe(event: string, callback: Function) {
if (!this.subscribers.has(event)) {
this.subscribers.set(event, new Set());
}
this.subscribers.get(event)!.add(callback);
}
publish(event: string, data: any) {
this.subscribers.get(event)?.forEach(cb => cb(data));
}
}
3. Styles qui se marchent dessus
Utilisez CSS-in-JS avec scope, CSS Modules, ou Shadow DOM pour éviter les conflits.
Patterns éprouvés
1. Shell application
Une app principale qui charge les micro-frontends :
// Shell app
const routes = [
{ path: '/users/*', component: lazy(() => import('users/UserApp')) },
{ path: '/products/*', component: lazy(() => import('products/ProductApp')) }
];
2. Shared Design System
Créez un package de composants partagés. C’est non-négociable pour la cohérence UX.
3. Contrat d’interface clair
Chaque MFE expose une interface claire :
interface MicroFrontend {
bootstrap(): Promise<void>;
mount(container: HTMLElement): Promise<void>;
unmount(): Promise<void>;
}
Quand NE PAS utiliser les micro-frontends
- Équipe < 10 développeurs
- Pas de domaines métiers clairement séparés
- Performance critique (chaque milliseconde compte)
- Projet court terme (<2 ans)
Pour ces cas, un bon monolithe modulaire est souvent meilleur.
Conclusion
Les micro-frontends sont puissants mais complexes. J’ai vu des projets tripler leur time-to-market grâce à cette architecture, et d’autres s’enliser dans la complexité.
La clé : commencer simple, scaler quand le besoin est réel, pas anticipé.
Temps de lecture : 7 minutes