Concepto clave
Module Federation es una funcionalidad de Webpack 5 que permite cargar código de aplicaciones independientes en tiempo de ejecución, creando un ecosistema de micro-frontends. Imagina un centro comercial donde cada tienda (micro-frontend) opera de forma autónoma, pero comparte servicios comunes como estacionamiento (dependencias) y permite a los clientes moverse entre ellas sin reiniciar su experiencia. En términos técnicos, Module Federation habilita la composición dinámica de bundles entre diferentes builds de Webpack, eliminando la necesidad de monorepos o builds monolíticos.
La arquitectura se basa en dos roles principales: host (aplicación contenedora que consume módulos remotos) y remote (aplicación que expone módulos). La magia ocurre mediante un manifesto de federación que Webpack genera automáticamente, describiendo qué módulos están disponibles y cómo acceder a ellos. Esto permite que equipos distribuidos desarrollen y desplieguen independientemente, mientras mantienen una experiencia de usuario cohesiva.
Cómo funciona en la práctica
Configurar Module Federation implica tres pasos principales en tu archivo webpack.config.js:
- Definir la configuración de federación usando el plugin ModuleFederationPlugin
- Especificar qué módulos expones (remotes) o consumes (host)
- Configurar las dependencias compartidas para evitar duplicación de librerías
Veamos un ejemplo básico: supongamos que tienes una aplicación de dashboard (host) que necesita incorporar un widget de gráficos desde una aplicación separada (remote). Primero, en la aplicación remote, configuras Webpack para exponer el componente ChartWidget. Luego, en el host, configuras la referencia a ese remote y lo importas dinámicamente en tu código. Webpack se encarga de resolver las dependencias y cargar el código cuando sea necesario, sin requerir un deploy conjunto.
Código en acción
Configuración de una aplicación remote que expone un componente:
// webpack.config.js de la aplicación remote (widget-app)
const { ModuleFederationPlugin } = require('webpack').container;
module.exports = {
// ... otras configuraciones de Webpack
plugins: [
new ModuleFederationPlugin({
name: 'widgetApp',
filename: 'remoteEntry.js',
exposes: {
'./ChartWidget': './src/components/ChartWidget.jsx',
'./DashboardHeader': './src/components/DashboardHeader.jsx'
},
shared: {
react: { singleton: true, eager: false },
'react-dom': { singleton: true, eager: false }
}
})
]
};
Configuración del host que consume el módulo remoto:
// webpack.config.js de la aplicación host (main-dashboard)
const { ModuleFederationPlugin } = require('webpack').container;
module.exports = {
// ... otras configuraciones
plugins: [
new ModuleFederationPlugin({
name: 'mainDashboard',
remotes: {
widgetApp: 'widgetApp@http://localhost:3001/remoteEntry.js'
},
shared: {
react: { singleton: true, eager: false },
'react-dom': { singleton: true, eager: false },
'chart.js': { singleton: true }
}
})
]
};
Uso en el código del host con importación dinámica:
// En main-dashboard/src/App.jsx
import React, { Suspense } from 'react';
const RemoteChartWidget = React.lazy(() => import('widgetApp/ChartWidget'));
function App() {
return (
Dashboard Principal
Cargando widget...}>
);
}
Errores comunes
- Versiones incompatibles de dependencias compartidas: Si host y remote usan diferentes versiones de React sin configurar correctamente el shared, pueden ocurrir errores de runtime. Solución: Usa singleton: true y especifica versiones requeridas en shared.
- Configuración incorrecta de CORS: Al desarrollar localmente, los remotes en diferentes puertos pueden bloquearse por políticas de same-origin. Solución: Configura headers adecuados en el servidor de desarrollo o usa un proxy.
- Falta de manejo de carga asíncrona: No envolver los componentes remotos en Suspense puede causar errores cuando el módulo no se ha cargado. Solución: Siempre usa React.lazy con Suspense y proporciona un fallback.
- Nombres de módulos mal referenciados: Usar './ChartWidget' en el host cuando el remote expone './ChartWidget' con diferente casing. Solución: Verifica que los paths en exposes y imports coincidan exactamente.
- Omitir eager: false en shared: Esto puede causar que las dependencias se carguen duplicadas. Solución: Configura eager: false para cargar bajo demanda, mejorando el performance inicial.
Checklist de dominio
- He configurado correctamente ModuleFederationPlugin en al menos una aplicación host y una remote
- Puedo exponer y consumir múltiples módulos entre aplicaciones independientes
- He implementado shared dependencies para librerías comunes como React y ReactDOM
- Sé cómo depurar problemas de carga de módulos usando las herramientas de desarrollador del navegador
- Puedo explicar la diferencia entre singleton y eager en la configuración de shared
- He configurado entornos de desarrollo con múltiples remotes en diferentes puertos
- Entiendo cómo Module Federation afecta el bundle final y las estrategias de caching
Crear un ecosistema de micro-frontends con Module Federation
En este ejercicio, configurarás dos aplicaciones React independientes que se comunican mediante Module Federation. Sigue estos pasos:
- Crea dos nuevas aplicaciones React usando Create React App (CRA) o tu setup preferido. Nómbralas app-host y app-remote.
- En app-remote, ejecuta
npm install webpack@5 webpack-cli webpack-dev-serverpara asegurar Webpack 5. Luego, configura webpack.config.js para exponer un componente llamadoWelcomeMessageque reciba una propusery muestre un saludo personalizado. - En app-host, configura webpack.config.js para consumir el módulo remoto de app-remote. Asegúrate de compartir React y ReactDOM como dependencias singleton.
- En app-host, crea un componente principal que importe dinámicamente WelcomeMessage desde app-remote y lo renderice pasando
user="Arquitecto"como prop. - Inicia ambos servidores de desarrollo en puertos diferentes (ej: 3000 y 3001) y verifica que el componente remoto se renderice correctamente en el host.
- Opcional: Modifica el componente WelcomeMessage en app-remote y observa cómo los cambios se reflejan en el host sin necesidad de reiniciar o redeployar.
- Recuerda que CRA puede requerir eject o usar craco para personalizar Webpack 5
- Asegúrate de que remoteEntry.js sea accesible desde el host verificando la URL en remotes
- Usa React.lazy y Suspense para manejar la carga asíncrona del módulo remoto
Evalua tu comprension
Completa el quiz interactivo de arriba para ganar XP.