Micro-frontends : Quand et comment les utiliser

Architecture Micro-frontends Scalabilité

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

← Retour au blog