Essayez sans attendre l'hébergement proposé par WordPress
-15% sur le premier mois avec le code 2025PRESS15AFF

Essayer maintenant

Architecture WordPress : Micro-frontends avec Module Federation et Webpack 5

Vous êtes-vous déjà demandé comment découper proprement une interface WordPress complexe quand plusieurs équipes travaillent dessus ? Les micro-frontends avec Module Federation de Webpack 5 offrent une solution élégante pour créer des applications WordPress modulaires et indépendantes. On va voir ensemble comment mettre en place cette architecture moderne qui révolutionne la façon de développer des sites WordPress à grande échelle.

Comprendre les micro-frontends : concepts et enjeux pour WordPress

Quand on développe des projets WordPress complexes depuis plusieurs années, on finit forcément par se heurter aux mêmes problèmes : équipes qui se marchent dessus, déploiements qui tournent au cauchemar, et cette fameuse phrase « ça marchait sur mon environnement ». C’est là que l’architecture micro-frontend prend tout son sens.

Qu’est-ce qu’un micro-frontend et pourquoi l’adopter ?

Concrètement, un micro-frontend, c’est une approche qui découpe votre interface utilisateur en plusieurs applications indépendantes. Au lieu d’avoir un énorme thème WordPress monolithique, on va séparer par exemple l’espace d’administration, la partie blog public, et l’espace membre en trois applications distinctes. Chacune peut être développée, testée et déployée séparément.

L’avantage principal ? Fini les conflits entre équipes ! L’équipe qui bosse sur l’espace membre peut pousser ses modifications sans impacter celle qui travaille sur le blog. Et techniquement, chaque micro-frontend peut utiliser sa propre stack : React pour l’admin, Vue.js pour l’espace membre, du vanilla JS pour le blog… La liberté totale.

Les défis spécifiques à WordPress

Maintenant, soyons réalistes : WordPress n’a pas été pensé pour ça. Le système de thèmes et plugins crée des dépendances partout. Vous modifiez functions.php ? Ça peut péter trois plugins. Vous mettez à jour un plugin ? Votre thème custom peut exploser.

J’ai vécu ça sur un projet e-commerce avec WooCommerce : équipe front qui voulait moderniser l’interface, équipe back qui devait maintenir l’existant, équipe marketing qui voulait tester de nouveaux funnels. Résultat : trois mois de développement pour une fonctionnalité qui aurait dû prendre trois semaines, parce qu’on passait notre temps à résoudre les conflits entre nos modifications.

Autre problème typique : la gestion des assets. Avec WordPress, tout passe par wp_enqueue_script et wp_enqueue_style. Difficile de gérer des bundles modernes, du code splitting, ou des imports dynamiques dans ce contexte.

Module Federation : la solution Webpack 5

Et c’est là que Module Federation change la donne ! Cette feature de Webpack 5 permet de partager du code entre applications à l’exécution, pas au build. Concrètement, votre micro-frontend « espace membre » peut utiliser des composants de votre micro-frontend « admin » sans avoir besoin de les recompiler.

Par rapport à d’autres approches comme les iframes (lents et limités) ou single-spa (complexe à configurer), Module Federation offre une intégration native. Pas besoin d’orchestrateur externe : Webpack gère tout. Et niveau performance, c’est du natif JavaScript, donc aucun overhead.

Le plus beau dans tout ça ? Ça reste compatible avec l’écosystème WordPress. Vos micro-frontends peuvent toujours communiquer avec l’API REST de WordPress, utiliser les hooks classiques, et même s’intégrer dans l’admin existante. C’est une migration progressive, pas une révolution.

Configuration Webpack 5 et Module Federation dans WordPress

Bon, passons aux choses sérieuses ! Maintenant qu’on a compris la théorie, il faut mettre les mains dans le code. La configuration de Module Federation avec WordPress, c’est un peu comme assembler un puzzle : chaque pièce a sa place et il faut respecter certaines règles.

Première chose importante : on va travailler avec deux types d’applications. L’application host (notre thème principal) et les applications remote (nos modules indépendants).

Structure de fichiers recommandée

Voici comment je structure généralement mes projets :

theme-parent/
├── webpack.config.js (host)
├── src/
│   ├── index.js
│   └── components/
├── modules/
│   ├── header-module/
│   │   ├── webpack.config.js (remote)
│   │   └── src/
│   ├── content-module/
│   │   ├── webpack.config.js (remote)
│   │   └── src/
│   └── sidebar-module/
│       ├── webpack.config.js (remote)
│       └── src/
└── functions.php

Cette structure permet de garder une organisation claire. Chaque module a sa propre configuration Webpack, ce qui facilite le développement indépendant.

Configuration du thème host

Le thème principal doit être configuré comme une application host. Voici un exemple de webpack.config.js complet :

const ModuleFederationPlugin = require("@module-federation/webpack");
const path = require("path");

module.exports = {
  mode: 'development',
  entry: './src/index.js',
  
  plugins: [
    new ModuleFederationPlugin({
      name: 'wpHost',
      remotes: {
        headerModule: 'headerModule@http://localhost:3001/remoteEntry.js',
        contentModule: 'contentModule@http://localhost:3002/remoteEntry.js',
        sidebarModule: 'sidebarModule@http://localhost:3003/remoteEntry.js'
      },
      shared: {
        react: { singleton: true, eager: true },
        'react-dom': { singleton: true, eager: true },
        '@wordpress/element': { singleton: true }
      }
    })
  ],
  
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'main.js',
    publicPath: get_template_directory_uri() + '/dist/'
  }
};

Attention : cette configuration suppose que vos modules remote tournent sur des ports différents en développement.

Configuration des modules remote

Pour un module header, voici la configuration type :

const ModuleFederationPlugin = require("@module-federation/webpack");

module.exports = {
  mode: 'development',
  entry: './src/index.js',
  
  plugins: [
    new ModuleFederationPlugin({
      name: 'headerModule',
      filename: 'remoteEntry.js',
      exposes: {
        './Header': './src/Header',
        './Navigation': './src/Navigation'
      },
      shared: {
        react: { singleton: true },
        'react-dom': { singleton: true },
        '@wordpress/element': { singleton: true }
      }
    })
  ],
  
  devServer: {
    port: 3001,
    headers: {
      "Access-Control-Allow-Origin": "*"
    }
  }
};

Le point crucial ici, c’est la section exposes : elle définit quels composants sont disponibles pour les autres applications.

Gestion des dépendances partagées

Les shared dependencies, c’est le nerf de la guerre ! WordPress utilise déjà React via @wordpress/element, donc il faut bien configurer le partage :

shared: {
  react: {
    singleton: true,
    eager: true,
    requiredVersion: '^17.0.0'
  },
  'react-dom': {
    singleton: true,
    eager: true,
    requiredVersion: '^17.0.0'
  },
  '@wordpress/element': {
    singleton: true,
    eager: true
  },
  '@wordpress/i18n': {
    singleton: true
  }
}

Le paramètre singleton: true garantit qu’une seule version de la librairie sera chargée. C’est essentiel pour éviter les conflits.

Intégration avec les hooks WordPress

Dans votre functions.php, vous devez enregistrer les scripts correctement :

function enqueue_module_federation_scripts() {
    wp_enqueue_script(
        'wp-host-app',
        get_template_directory_uri() . '/dist/main.js',
        array(),
        '1.0.0',
        true
    );
    
    // Configuration pour les URLs des remotes
    wp_localize_script('wp-host-app', 'wpModuleFederation', array(
        'headerModuleUrl' => get_option('header_module_url', 'http://localhost:3001/remoteEntry.js'),
        'contentModuleUrl' => get_option('content_module_url', 'http://localhost:3002/remoteEntry.js')
    ));
}
add_action('wp_enqueue_scripts', 'enqueue_module_federation_scripts');

Cette approche permet de rendre les URLs des modules configurables, ce qui est pratique pour différents environnements.

Gestion des assets CSS et images

Pour les assets statiques, il faut adapter la configuration Webpack. Voici comment je gère généralement les images :

module.exports = {
  // ... configuration précédente
  module: {
    rules: [
      {
        test: /\.(png|jpe?g|gif|svg)$/i,
        type: 'asset/resource',
        generator: {
          publicPath: get_template_directory_uri() + '/dist/images/',
          outputPath: 'images/'
        }
      },
      {
        test: /\.css$/i,
        use: ['style-loader', 'css-loader']
      }
    ]
  }
};

Pour le CSS, j’ai tendance à laisser style-loader s’en occuper. Ça injecte directement les styles dans le DOM, ce qui évite les conflits avec les styles WordPress existants.

Exemple concret : thème découpé en modules

Concrètement, voici comment utiliser nos modules dans le thème :

// src/index.js du thème host
import React, { Suspense } from 'react';
import { render } from '@wordpress/element';

const RemoteHeader = React.lazy(() => import('headerModule/Header'));
const RemoteContent = React.lazy(() => import('contentModule/Content'));
const RemoteSidebar = React.lazy(() => import('sidebarModule/Sidebar'));

function App() {
  return (
    <div className="wp-theme-container">
      <Suspense fallback={<div>Chargement header...</div>}>
        <RemoteHeader />
      </Suspense>
      
      <main className="content-area">
        <Suspense fallback={<div>Chargement contenu...</div>}>
          <RemoteContent />
        </Suspense>
        
        <aside className="sidebar">
          <Suspense fallback={<div>Chargement sidebar...</div>}>
            <RemoteSidebar />
          </Suspense>
        </aside>
      </main>
    </div>
  );
}

const container = document.getElementById('wp-micro-frontend-root');
if (container) {
  render(<App />, container);
}

N’oubliez pas : le Suspense est obligatoire pour les imports dynamiques. Et pensez à ajouter des fallbacks sympas pour l’expérience utilisateur !

Découpage logique et gestion du state entre modules

Architecture des composants WordPress modulaires

Bon, entrons dans le vif du sujet ! Le secret d’une architecture micro-frontends réussie, c’est de découper selon la logique métier plutôt que par type de fichier. Fini les dossiers css/, js/ et templates/ à l’ancienne.

J’organise mes modules par domaine fonctionnel : un module user-profile, un autre product-catalog, et ainsi de suite. Chaque module contient ses propres styles, scripts, et templates. Cette approche facilite énormément la maintenance – quand je dois modifier la gestion du profil utilisateur, tout est centralisé dans un seul endroit.

Pour WordPress spécifiquement, je recommande cette structure :

modules/
├── checkout/
│   ├── assets/
│   ├── components/
│   └── hooks/
├── user-dashboard/
└── product-reviews/

Chaque module expose ses fonctionnalités via des interfaces claires et peut être développé de façon totalement autonome.

Communication inter-modules et gestion d’état

La communication entre modules, c’est le nerf de la guerre ! J’ai testé plusieurs approches et je vous partage ce qui fonctionne vraiment bien.

Pour les échanges simples, les Custom Events du DOM font parfaitement l’affaire :

// Module checkout
document.dispatchEvent(new CustomEvent('checkout:completed', {
  detail: { orderId: 12345, total: 89.99 }
}));

// Module user-dashboard
document.addEventListener('checkout:completed', (event) => {
  updateOrderHistory(event.detail);
});

Pour un state management plus complexe, Zustand s’intègre parfaitement. Plus léger que Redux, il permet de créer des stores partagés entre modules sans trop de complexité.

Attention cependant : évitez les couplages trop forts entre modules. Si un module A ne peut pas fonctionner sans le module B, c’est que votre découpage n’est peut-être pas optimal.

Résolution des conflits de dépendances

Ah, les conflits de dépendances… Le cauchemar de tout développeur ! Avec Module Federation, on peut heureusement gérer ça proprement.

Le problème classique : votre module checkout utilise React 18, mais le module user-dashboard tourne encore sur React 17. Webpack 5 permet de définir des stratégies de partage intelligentes :

shared: {
  'react': {
    singleton: true,
    requiredVersion: '^17.0.0',
    eager: false
  },
  'jquery': {
    singleton: true,
    requiredVersion: false // Accepte n'importe quelle version
  }
}

Pour jQuery (omniprésent dans l’écosystème WordPress), je recommande de toujours utiliser singleton: true. Ça évite d’avoir plusieurs instances qui se marchent sur les pieds.

Et si vraiment un conflit est impossible à résoudre ? On peut isoler complètement un module en désactivant le partage pour cette dépendance spécifique.

Stratégies de déploiement indépendant

Le déploiement indépendant, c’est là que les micro-frontends montrent leur vraie valeur ! Chaque module peut avoir son propre cycle de vie.

J’utilise un système de versioning sémantique pour chaque module. Le module WooCommerce checkout par exemple :

// webpack.config.js du module checkout
module.exports = {
  plugins: [
    new ModuleFederationPlugin({
      name: 'wooCheckout',
      filename: 'remoteEntry.js',
      library: { type: 'var', name: 'wooCheckout_v2_1_0' },
      exposes: {
        './CheckoutForm': './src/CheckoutForm',
        './PaymentMethods': './src/PaymentMethods'
      }
    })
  ]
};

Côté WordPress, j’ai développé un petit plugin qui gère le mapping des versions :

add_action('wp_enqueue_scripts', function() {
    $module_version = get_option('checkout_module_version', '2.1.0');
    wp_enqueue_script(
        'checkout-module',
        "/modules/checkout/v{$module_version}/remoteEntry.js",
        [],
        $module_version
    );
});

Le rollback devient un jeu d’enfant : il suffit de changer la version dans la base de données ! Et pour les tests d’intégration, je configure des environnements de staging où chaque module peut être testé avec différentes versions des autres modules.

Bref, cette approche nous donne une flexibilité énorme sans sacrifier la stabilité.