Implementar Componentes Federados y Comunicación

Lectura
30 min~5 min lectura

Concepto clave

Los componentes federados son módulos de UI que se desarrollan, despliegan y ejecutan de forma independiente, pero que pueden ser consumidos dinámicamente por otras aplicaciones en tiempo de ejecución. Imagina una ciudad donde cada edificio (micro-frontend) tiene su propia administración y reglas, pero comparten servicios públicos (componentes federados) como parques o bibliotecas que cualquier residente puede usar sin necesidad de construirlos desde cero.

La comunicación entre micro-frontends es crucial para mantener la coherencia del estado y la experiencia de usuario. En lugar de acoplar las aplicaciones, se establecen contratos claros mediante eventos, props compartidos o un estado global ligero. Piensa en cómo los departamentos de una empresa colaboran: cada uno tiene autonomía, pero se coordinan mediante reuniones (eventos) y documentos compartidos (estado), evitando interferir en los procesos internos del otro.

Cómo funciona en la práctica

Para implementar componentes federados, primero defines un remote en Webpack que expone módulos específicos. Luego, en la aplicación host, configuras un remotes para consumir esos módulos. La magia ocurre en tiempo de ejecución: cuando el host necesita un componente, Webpack lo carga dinámicamente desde el remote, inyectándolo en la aplicación como si fuera local.

La comunicación se maneja típicamente mediante un bus de eventos o un store compartido. Por ejemplo, si un componente federado de carrito de compras necesita notificar a la aplicación host sobre un cambio, emite un evento custom. El host escucha ese evento y actualiza su estado o UI correspondiente. Esto mantiene el desacoplamiento: el componente no necesita saber detalles de implementación del host, solo sigue el contrato definido.

Codigo en accion

Configuración en el remote (app-productos):

// webpack.config.js del remote
const ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin");

module.exports = {
  plugins: [
    new ModuleFederationPlugin({
      name: "productos",
      filename: "remoteEntry.js",
      exposes: {
        "./ProductCard": "./src/components/ProductCard",
        "./ProductList": "./src/components/ProductList"
      },
      shared: ["react", "react-dom"]
    })
  ]
};

Consumo en el host (app-principal):

// webpack.config.js del host
const ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin");

module.exports = {
  plugins: [
    new ModuleFederationPlugin({
      name: "principal",
      remotes: {
        productos: "productos@http://localhost:3001/remoteEntry.js"
      },
      shared: ["react", "react-dom"]
    })
  ]
};

// Uso en un componente React del host
import React, { Suspense } from 'react';
const ProductCard = React.lazy(() => import("productos/ProductCard"));

function App() {
  return (
    Cargando...}>
       {
        // Emitir evento o actualizar store compartido
        window.dispatchEvent(new CustomEvent('cart-update', { detail: product }));
      }} />
    
  );
}

Errores comunes

  • Version mismatch de dependencias compartidas: Si el remote y el host usan versiones diferentes de React, pueden ocurrir errores críticos. Solución: Usa shared en Webpack con versiones exactas o rangos compatibles, y considera herramientas como singleton para bibliotecas clave.
  • Comunicación acoplada: Implementar comunicación directa entre componentes (ej., llamadas a funciones internas) rompe la independencia. Solución: Usa eventos custom o un store global ligero (como Zustand o Redux con namespaces) que defina contratos claros.
  • Falta de manejo de errores en carga dinámica: Si el remote falla, la aplicación host puede bloquearse. Solución: Envuelve los imports dinámicos en Error Boundaries en React y provee fallbacks UX.
  • Omitir el lazy loading: Cargar todos los remotes al inicio impacta el performance. Solución: Usa React.lazy o import() dinámico solo cuando el componente sea necesario.
  • Configuración incorrecta de CORS: En entornos de desarrollo o producción, los remotes pueden bloquearse por políticas de seguridad. Solución: Configura headers CORS apropiados en el servidor del remote y usa HTTPS en producción.

Checklist de dominio

  1. Configuré exitosamente un remote que expone al menos dos componentes federados.
  2. Implementé un host que consume dinámicamente componentes de múltiples remotes.
  3. Establecí un mecanismo de comunicación (eventos o store) entre host y remotes sin acoplamiento.
  4. Manejé errores de carga y versiones en dependencias compartidas.
  5. Optimicé el performance con lazy loading y chunk splitting.
  6. Probé la arquitectura en un entorno local y simulado de producción.
  7. Documenté los contratos de comunicación para otros equipos.

Crear un ecosistema de micro-frontends con carrito federado

En este ejercicio, construirás un sistema donde una app principal (host) consume componentes de dos remotes independientes: uno para productos y otro para carrito, con comunicación en tiempo real.

  1. Configura tres proyectos React independientes: app-host, app-productos, y app-carrito. Usa Webpack 5 con Module Federation.
  2. En app-productos, expone un componente ProductList que muestre una lista de productos. Cada producto debe tener un botón "Agregar al carrito".
  3. En app-carrito, expone un componente CartSummary que muestre el número de ítems en el carrito y un total.
  4. En app-host, consume ambos componentes y renderízalos en una misma página. Implementa comunicación: cuando se hace clic en "Agregar al carrito" en ProductList, el CartSummary debe actualizarse automáticamente.
  5. Usa un bus de eventos global (puedes implementar uno simple con window.dispatchEvent y window.addEventListener) para la comunicación. Asegúrate de que los componentes no tengan dependencias directas entre sí.
  6. Prueba el sistema: ejecuta los tres proyectos en puertos diferentes (ej., 3000, 3001, 3002) y verifica que la comunicación funcione sin errores.
Pistas
  • Usa shared: ['react', 'react-dom'] en todas las configuraciones de Module Federation para evitar versiones duplicadas.
  • Para el bus de eventos, define un nombre de evento constante (ej., 'cart-updated') y pasa datos en el detail del CustomEvent.
  • Considera usar Suspense y fallbacks en el host para manejar la carga asíncrona de componentes federados.

Evalua tu comprension

Completa el quiz interactivo de arriba para ganar XP.