{"schemaVersion":1,"id":"https://lucioduran.com/es/blog/micro-frontends-module-federation-native-federation","url":"https://lucioduran.com/es/blog/micro-frontends-module-federation-native-federation","title":"Micro-Frontends en 2026: Module Federation 2.0, Native Federation e Import Maps","description":"Guía exhaustiva de arquitectura micro-frontend en 2026, Module Federation 2.0, Native Federation, import maps, manejo de estado compartido, y una migración documentada de monolito SPA a frontends deployables independientemente.","language":"es","datePublished":"2025-11-02","dateModified":null,"contentFormat":"text/markdown","content":"Frontend\n\n# Micro-Frontends en 2026: Module Federation 2.0, Native Federation e Import Maps\n\n2025-11-028 min lectura\n\nLucio Durán\nEngineering Manager & AI Solutions Architect\n\nTambién disponible en: [English](<https://lucioduran.com/blog/micro-frontends-module-federation-native-federation>)\n\n## La Decisión Arquitectónica\n\nTres enfoques merecen evaluación:\n\n1. **Module Federation 2.0** (via Rspack), Compartir módulos en runtime, ecosistema maduro, específico de webpack/rspack\n2. **Native Federation**, ES modules + import maps, agnóstico de bundler, más nuevo\n3. **Composición en build-time** (paquetes npm), Cada MFE publicado como paquete npm, compuesto en build time\n\n[Module Federation](<https://module-federation.io/>) 2.0 con Rspack para la aplicación core e import maps como capa de discovery representa la combinación más sólida.\n\n### Module Federation 2.0: Qué Cambió\n\nMF 2.0 (shipeado con Rspack 1.x y webpack 5.9x) introdujo tres cosas que lo hicieron realmente usable para producción:\n\n**1. Protocolo de manifest.** En vez de hardcodear URLs de remotes en la config de webpack, los remotes publican un ` mf-manifest.json ` que describe sus módulos expuestos, dependencias compartidas, y entry points:\n\n```json\n{\n \"id\": \"checkout-mfe\",\n \"name\": \"checkout\",\n \"metaData\": {\n \"buildHash\": \"a3f8c2d\",\n \"version\": \"2.14.0\",\n \"framework\": \"react@18.3.1\"\n },\n \"shared\": [\n {\n \"name\": \"react\",\n \"version\": \"18.3.1\",\n \"scope\": \"default\",\n \"strategy\": \"loaded-first\"\n }\n ],\n \"exposes\": [\n {\n \"path\": \"./CheckoutFlow\",\n \"name\": \"CheckoutFlow\",\n \"assets\": {\n \"js\": { \"async\": [\"checkout-flow.js\"], \"sync\": [] },\n \"css\": { \"async\": [\"checkout-flow.css\"], \"sync\": [] }\n }\n }\n ]\n}\n```\n\n**2. Runtime plugins.** Puede interceptar todo el ciclo de vida de federation, antes de cargar un remote, después de resolver dependencias compartidas, on error. Así se puede implementar A/B testing a nivel de micro-frontend:\n\n```javascript\n// host/federation-plugin.js\nimport { FederationRuntimePlugin } from '@module-federation/runtime';\n\nconst ABTestPlugin = () => ({\n name: 'ab-test-plugin',\n beforeRequest(args) {\n const { id, options } = args;\n const variant = getABVariant(id);\n if (variant) {\n options.remotes = options.remotes.map(remote => {\n if (remote.name === id) {\n return {\n ...remote,\n entry: remote.entry.replace(\n 'mf-manifest.json',\n `mf-manifest-${variant}.json`\n )\n };\n }\n return remote;\n });\n }\n return args;\n }\n});\n```\n\n**3. Generación de tipos.** MF 2.0 genera declaraciones TypeScript para módulos expuestos y las sincroniza entre micro-frontends via un manifest. No más ` // @ts-ignore ` en cada dynamic import.\n\n### La Aplicación Host\n\nEl host (shell) es mínimo, maneja routing, autenticación, y el chrome de navegación. Cada ruta mapea a un micro-frontend remoto:\n\n```javascript\n// host/src/App.jsx\nimport { lazy, Suspense } from 'react';\nimport { init, loadRemote } from '@module-federation/enhanced/runtime';\nimport { Routes, Route } from 'react-router-dom';\n\ninit({\n name: 'host',\n remotes: [\n { name: 'dashboard', entry: `${window.__MFE_CONFIG__.dashboard}/mf-manifest.json` },\n { name: 'checkout', entry: `${window.__MFE_CONFIG__.checkout}/mf-manifest.json` },\n { name: 'accounts', entry: `${window.__MFE_CONFIG__.accounts}/mf-manifest.json` },\n { name: 'settings', entry: `${window.__MFE_CONFIG__.settings}/mf-manifest.json` }\n ],\n shared: {\n react: { singleton: true, requiredVersion: '^18.3.0' },\n 'react-dom': { singleton: true, requiredVersion: '^18.3.0' },\n 'react-router-dom': { singleton: true, requiredVersion: '^6.20.0' }\n }\n});\n\nconst Dashboard = lazy(() => loadRemote('dashboard/DashboardApp'));\nconst Checkout = lazy(() => loadRemote('checkout/CheckoutFlow'));\nconst Accounts = lazy(() => loadRemote('accounts/AccountsApp'));\nconst Settings = lazy(() => loadRemote('settings/SettingsApp'));\n\nfunction App() {\n return (\n <Shell>\n <Suspense fallback={<MFELoadingSkeleton />}>\n <Routes>\n <Route path=\"/dashboard/*\" element={<Dashboard />} />\n <Route path=\"/checkout/*\" element={<Checkout />} />\n <Route path=\"/accounts/*\" element={<Accounts />} />\n <Route path=\"/settings/*\" element={<Settings />} />\n </Routes>\n </Suspense>\n </Shell>\n );\n}\n```\n\nEl objeto ` window.__MFE_CONFIG__ ` lo inyecta el pipeline de deployment. En producción, apunta a URLs de CDN. En desarrollo, los desarrolladores pueden overridear entries específicas para apuntar a ` localhost:3001 `, ` localhost:3002 `, etc.\n\n## Import Maps como Capa de Discovery\n\nAcá encajan los import maps. En vez de hardcodear URLs de MFEs en el bundle JavaScript del host, servimos un import map desde un CDN que el host lee al arrancar:\n\n```html\n<script type=\"importmap\">\n{\n \"imports\": {\n \"dashboard-mfe\": \"https://cdn.example.com/mfe/dashboard/v2.14.0/\",\n \"checkout-mfe\": \"https://cdn.example.com/mfe/checkout/v3.2.1/\",\n \"accounts-mfe\": \"https://cdn.example.com/mfe/accounts/v1.8.0/\",\n \"settings-mfe\": \"https://cdn.example.com/mfe/settings/v2.0.0/\"\n }\n}\n</script>\n```\n\nEste import map lo genera el pipeline de CI cada vez que un MFE deployea. La ventaja crítica: es posible actualizar qué versión de un MFE se sirve sin redeployar la aplicación host. ¿Deployeaste checkout v3.2.2? Solo actualizá el import map. ¿Rollback? Actualizá el import map de nuevo. El host nunca cambia.\n\n## Estado Compartido: El Problema Difícil\n\nAcá es donde la mayoría de las arquitecturas micro-frontend se caen a pedazos. Cuatro equipos, siete MFEs, y todos necesitan saber quién es el usuario actual, cuáles son sus permisos, y si el carrito tiene items.\n\nLa tentación es compartir un store Redux. Esto es desaconsejable. Un store compartido crea acoplamiento implícito entre MFEs, el equipo A cambia un reducer, los selectors del equipo B se rompen, y nadie lo atrapa hasta producción porque deployean independientemente.\n\nEn su lugar, un enfoque de tres capas funciona bien:\n\n### Capa 1: Contexto de Autenticación (Del Host)\n\nEl host es dueño del estado de autenticación y lo expone via un event bus custom:\n\n```javascript\n// host/src/auth-bus.js\nclass AuthBus {\n #listeners = new Set();\n #currentUser = null;\n\n setUser(user) {\n this.#currentUser = Object.freeze(user);\n this.#notify('user-changed', this.#currentUser);\n }\n\n getUser() { return this.#currentUser; }\n\n subscribe(callback) {\n this.#listeners.add(callback);\n if (this.#currentUser) {\n callback({ type: 'user-changed', payload: this.#currentUser });\n }\n return () => this.#listeners.delete(callback);\n }\n\n #notify(type, payload) {\n const event = Object.freeze({ type, payload });\n this.#listeners.forEach(cb => cb(event));\n }\n}\n\nwindow.__AUTH_BUS__ = new AuthBus();\n```\n\n### Capa 2: Eventos Cross-MFE (BroadcastChannel)\n\nPara eventos que cualquier MFE puede producir y cualquier otro consumir (como \"item agregado al carrito\"), usamos ` BroadcastChannel `:\n\n```javascript\n// shared-lib/src/mfe-events.js\nconst channel = new BroadcastChannel('mfe-events');\n\nexport function emitMFEEvent(type, payload, source) {\n channel.postMessage({ type, payload, source, timestamp: Date.now() });\n}\n\nexport function onMFEEvent(type, callback) {\n const handler = (event) => {\n if (event.data.type === type) callback(event.data);\n };\n channel.addEventListener('message', handler);\n return () => channel.removeEventListener('message', handler);\n}\n```\n\n### Capa 3: URL como Estado (Router-Driven)\n\nEl estado de navegación compartido vive en la URL. Si el dashboard necesita linkear a una cuenta específica, navega a ` /accounts/ACC-123 `. El MFE de accounts lee sus route params. Sin estado compartido necesario.\n\n## La Migración: Patrón Strangler Fig\n\nEl monolito no se reescribió. Se estranguló. Acá va un timeline representativo:\n\n**Mes 1-2**: Levantar el host shell, infraestructura de Module Federation, y pipeline de CI/CD. El host renderizaba el monolito entero como un solo remote \"legacy\".\n\n**Mes 3-4**: Se extrajo la página de settings (menor riesgo, menos dependencias) a su propio MFE. Dos ingenieros, dos semanas de código real. El resto fue plomería de CI/CD.\n\n**Mes 5-6**: Se extrajo checkout (mayor valor de negocio, más complejo). Esto requirió la arquitectura de estado compartido descrita arriba. Tres ingenieros, seis semanas.\n\n**Mes 7-10**: Se extrajeron dashboard y accounts en paralelo. A esa altura, los patrones estaban establecidos y cada extracción tomó 3-4 semanas por equipo.\n\n**Mes 11-12**: Se decomisionó el remote del monolito legacy. Lo que quedó fue el host shell + cuatro MFEs independientes.\n\n### Errores Comunes\n\n1. **Aislamiento CSS.** Sin aislamiento CSS enforceado desde el día uno, los estilos globales del MFE A se filtran al MFE B. La solución: Shadow DOM para MFEs nuevos y un plugin PostCSS que scopea todos los selectores para código legacy.\n2. **Drift de versiones de dependencias compartidas.** En tres meses, dos MFEs estaban en React 18.3.1 y dos en 18.2.0. El singleton mode de Module Federation hizo que los hooks de un equipo fallaran silenciosamente. Agregar un check de CI que falle si las versiones de dependencias compartidas divergen más de un minor previene esto.\n3. **Error boundaries.** Cuando un MFE remoto falla al cargar (caída de CDN, problema de red), todo el host crasheaba porque no wrapeamos ` loadRemote() ` en error boundaries apropiados. Ahora cada punto de montaje de MFE tiene un ` <MFEErrorBoundary> ` que muestra una UI de retry.\n\n## Impacto en Performance\n\nMétricas clave de performance:\n\n| Métrica | Monolito | Micro-Frontends | Delta |\n| --- | --- | --- | --- |\n| Bundle JS inicial | 2.4MB (gzipped 680KB) | 320KB host + MFEs lazy | -53% carga inicial |\n| Time to Interactive | 4.2s | 1.8s (host) + 0.6s (primer MFE) | -43% |\n| Tiempo de deploy | 22 min (build + E2E completo) | 3-4 min por MFE | -82% |\n| Frecuencia de deploy | 2-3/día (todos los equipos combinados) | 3-5/día por equipo | +400% |\n\nLa carga inicial mejoró porque los usuarios solo descargan el código de la página que visitan. Los 1.8MB de librerías de charting del equipo de dashboard no se descargan hasta que alguien visita ` /dashboard `.\n\nEl tradeoff: hay un breve estado de loading al navegar entre MFEs por primera vez mientras el bundle remoto se descarga. Precargar el MFE más probable siguiente basándose en patrones de navegación del usuario, lo que mitiga esto para el 80% de las transiciones.\n\n## Cuándo Tienen Sentido los Micro-Frontends\n\nLos micro-frontends se justifican solo para organizaciones con la estructura de equipo que lo demande. Los micro-frontends son una solución organizacional a un problema organizacional. Con un equipo, un monolito es lo indicado. Con dos equipos, un monorepo con code ownership. Con cuatro o más equipos que necesitan cadencias de deploy independientes, los micro-frontends valen la complejidad.\n\nLa tecnología finalmente está lo suficientemente madura como para que el dolor de implementación sea manejable. La guía: arrancar con un monolito. Extraer cuando el dolor organizacional lo demande. Y al extraer, usar el patrón strangler fig. Es el único approach de migración que te deja shipear features a los usuarios cada semana mientras la descomposición pasa en el fondo.\n\n## Preguntas frecuentes\n\n### ¿Cuál es la diferencia entre Module Federation y Native Federation?\n\nModule Federation es un plugin de webpack/rspack que habilita compartir módulos en runtime entre aplicaciones buildeadas separadamente. Native Federation logra lo mismo pero usando ES modules nativos del navegador e import maps en vez de un runtime específico del bundler, haciéndolo agnóstico de framework y bundler.\n\n### ¿Debería usar micro-frontends para mi proyecto?\n\nSolo si teners múltiples equipos que necesitan deployar cambios de frontend independientemente. Para un solo equipo o una aplicación chica, los micro-frontends agregan complejidad significativa con beneficio mínimo. El punto óptimo es 3+ equipos, 50k+ líneas de código frontend, y una cadencia de deploy que varía por equipo.\n\n### ¿Cómo manejan los micro-frontends dependencias compartidas como React?\n\nModule Federation 2.0 lo maneja via su configuración shared, que negocia versiones compatibles en runtime. Native Federation usa import maps para apuntar todos los micro-frontends a la misma URL de dependencia singleton. Ambos enfoques funcionan pero tienen modos de falla diferentes cuando las versiones divergen.\n\n## Tips\n\n1. **Tip:** Nunca compartas React via la config ` shared ` de Module Federation con ` singleton: true ` a menos que TODOS tus remotes estén en la misma versión exacta de React. Un mismatch de versión con singleton mode causa fallas silenciosas de hooks que son casi imposibles de debuggear en producción.\n2. **Tip:** Utilizar import maps como tu capa de discovery de micro-frontends aunque uses Module Federation para la carga. Overrideá el import map en staging para apuntar remotes específicos a localhost para el loop de desarrollo más rápido posible.\n3. **Tip:** Para estado compartido entre micro-frontends, utilizar la API ` BroadcastChannel ` del navegador para loose coupling en vez de un store Redux compartido. Cada MFE es dueño de su propio estado, la comunicación cross-MFE pasa por eventos, no por memoria compartida.\n\n- micro-frontends\n- module-federation\n- native-federation\n- import-maps\n- webpack\n- rspack\n- estado-compartido\n- arquitectura-frontend\n\n[Volver al Blog](<https://lucioduran.com/es/blog>)","headings":[{"level":1,"text":"Micro-Frontends en 2026: Module Federation 2.0, Native Federation e Import Maps","url":"https://lucioduran.com/es/blog/micro-frontends-module-federation-native-federation#title"},{"level":2,"text":"La Decisión Arquitectónica","url":"https://lucioduran.com/es/blog/micro-frontends-module-federation-native-federation#la-decisin-arquitectnica"},{"level":3,"text":"Module Federation 2.0: Qué Cambió","url":"https://lucioduran.com/es/blog/micro-frontends-module-federation-native-federation#module-federation-20-qu-cambi"},{"level":3,"text":"La Aplicación Host","url":"https://lucioduran.com/es/blog/micro-frontends-module-federation-native-federation#la-aplicacin-host"},{"level":2,"text":"Import Maps como Capa de Discovery","url":"https://lucioduran.com/es/blog/micro-frontends-module-federation-native-federation#import-maps-como-capa-de-discovery"},{"level":2,"text":"Estado Compartido: El Problema Difícil","url":"https://lucioduran.com/es/blog/micro-frontends-module-federation-native-federation#estado-compartido-el-problema-difcil"},{"level":3,"text":"Capa 1: Contexto de Autenticación (Del Host)","url":"https://lucioduran.com/es/blog/micro-frontends-module-federation-native-federation#capa-1-contexto-de-autenticacin-del-host"},{"level":3,"text":"Capa 2: Eventos Cross-MFE (BroadcastChannel)","url":"https://lucioduran.com/es/blog/micro-frontends-module-federation-native-federation#capa-2-eventos-cross-mfe-broadcastchannel"},{"level":3,"text":"Capa 3: URL como Estado (Router-Driven)","url":"https://lucioduran.com/es/blog/micro-frontends-module-federation-native-federation#capa-3-url-como-estado-router-driven"},{"level":2,"text":"La Migración: Patrón Strangler Fig","url":"https://lucioduran.com/es/blog/micro-frontends-module-federation-native-federation#la-migracin-patrn-strangler-fig"},{"level":3,"text":"Errores Comunes","url":"https://lucioduran.com/es/blog/micro-frontends-module-federation-native-federation#errores-comunes"},{"level":2,"text":"Impacto en Performance","url":"https://lucioduran.com/es/blog/micro-frontends-module-federation-native-federation#impacto-en-performance"},{"level":2,"text":"Cuándo Tienen Sentido los Micro-Frontends","url":"https://lucioduran.com/es/blog/micro-frontends-module-federation-native-federation#cundo-tienen-sentido-los-micro-frontends"},{"level":2,"text":"Preguntas frecuentes","url":"https://lucioduran.com/es/blog/micro-frontends-module-federation-native-federation#questions"},{"level":3,"text":"¿Cuál es la diferencia entre Module Federation y Native Federation?","url":"https://lucioduran.com/es/blog/micro-frontends-module-federation-native-federation#question-1"},{"level":3,"text":"¿Debería usar micro-frontends para mi proyecto?","url":"https://lucioduran.com/es/blog/micro-frontends-module-federation-native-federation#question-2"},{"level":3,"text":"¿Cómo manejan los micro-frontends dependencias compartidas como React?","url":"https://lucioduran.com/es/blog/micro-frontends-module-federation-native-federation#question-3"},{"level":2,"text":"Tips","url":"https://lucioduran.com/es/blog/micro-frontends-module-federation-native-federation#article-tips"}],"sections":[{"heading":"Micro-Frontends en 2026: Module Federation 2.0, Native Federation e Import Maps","level":1,"url":"https://lucioduran.com/es/blog/micro-frontends-module-federation-native-federation","content":"Frontend"},{"heading":"Micro-Frontends en 2026: Module Federation 2.0, Native Federation e Import Maps","level":1,"url":"https://lucioduran.com/es/blog/micro-frontends-module-federation-native-federation#title","content":"2025-11-028 min lectura\n\nLucio Durán\nEngineering Manager & AI Solutions Architect\n\nTambién disponible en: [English](<https://lucioduran.com/blog/micro-frontends-module-federation-native-federation>)"},{"heading":"La Decisión Arquitectónica","level":2,"url":"https://lucioduran.com/es/blog/micro-frontends-module-federation-native-federation#la-decisin-arquitectnica","content":"Tres enfoques merecen evaluación:\n\n1. **Module Federation 2.0** (via Rspack), Compartir módulos en runtime, ecosistema maduro, específico de webpack/rspack\n2. **Native Federation**, ES modules + import maps, agnóstico de bundler, más nuevo\n3. **Composición en build-time** (paquetes npm), Cada MFE publicado como paquete npm, compuesto en build time\n\n[Module Federation](<https://module-federation.io/>) 2.0 con Rspack para la aplicación core e import maps como capa de discovery representa la combinación más sólida."},{"heading":"Module Federation 2.0: Qué Cambió","level":3,"url":"https://lucioduran.com/es/blog/micro-frontends-module-federation-native-federation#module-federation-20-qu-cambi","content":"MF 2.0 (shipeado con Rspack 1.x y webpack 5.9x) introdujo tres cosas que lo hicieron realmente usable para producción:\n\n**1. Protocolo de manifest.** En vez de hardcodear URLs de remotes en la config de webpack, los remotes publican un ` mf-manifest.json ` que describe sus módulos expuestos, dependencias compartidas, y entry points:\n\n```json\n{\n \"id\": \"checkout-mfe\",\n \"name\": \"checkout\",\n \"metaData\": {\n \"buildHash\": \"a3f8c2d\",\n \"version\": \"2.14.0\",\n \"framework\": \"react@18.3.1\"\n },\n \"shared\": [\n {\n \"name\": \"react\",\n \"version\": \"18.3.1\",\n \"scope\": \"default\",\n \"strategy\": \"loaded-first\"\n }\n ],\n \"exposes\": [\n {\n \"path\": \"./CheckoutFlow\",\n \"name\": \"CheckoutFlow\",\n \"assets\": {\n \"js\": { \"async\": [\"checkout-flow.js\"], \"sync\": [] },\n \"css\": { \"async\": [\"checkout-flow.css\"], \"sync\": [] }\n }\n }\n ]\n}\n```\n\n**2. Runtime plugins.** Puede interceptar todo el ciclo de vida de federation, antes de cargar un remote, después de resolver dependencias compartidas, on error. Así se puede implementar A/B testing a nivel de micro-frontend:\n\n```javascript\n// host/federation-plugin.js\nimport { FederationRuntimePlugin } from '@module-federation/runtime';\n\nconst ABTestPlugin = () => ({\n name: 'ab-test-plugin',\n beforeRequest(args) {\n const { id, options } = args;\n const variant = getABVariant(id);\n if (variant) {\n options.remotes = options.remotes.map(remote => {\n if (remote.name === id) {\n return {\n ...remote,\n entry: remote.entry.replace(\n 'mf-manifest.json',\n `mf-manifest-${variant}.json`\n )\n };\n }\n return remote;\n });\n }\n return args;\n }\n});\n```\n\n**3. Generación de tipos.** MF 2.0 genera declaraciones TypeScript para módulos expuestos y las sincroniza entre micro-frontends via un manifest. No más ` // @ts-ignore ` en cada dynamic import."},{"heading":"La Aplicación Host","level":3,"url":"https://lucioduran.com/es/blog/micro-frontends-module-federation-native-federation#la-aplicacin-host","content":"El host (shell) es mínimo, maneja routing, autenticación, y el chrome de navegación. Cada ruta mapea a un micro-frontend remoto:\n\n```javascript\n// host/src/App.jsx\nimport { lazy, Suspense } from 'react';\nimport { init, loadRemote } from '@module-federation/enhanced/runtime';\nimport { Routes, Route } from 'react-router-dom';\n\ninit({\n name: 'host',\n remotes: [\n { name: 'dashboard', entry: `${window.__MFE_CONFIG__.dashboard}/mf-manifest.json` },\n { name: 'checkout', entry: `${window.__MFE_CONFIG__.checkout}/mf-manifest.json` },\n { name: 'accounts', entry: `${window.__MFE_CONFIG__.accounts}/mf-manifest.json` },\n { name: 'settings', entry: `${window.__MFE_CONFIG__.settings}/mf-manifest.json` }\n ],\n shared: {\n react: { singleton: true, requiredVersion: '^18.3.0' },\n 'react-dom': { singleton: true, requiredVersion: '^18.3.0' },\n 'react-router-dom': { singleton: true, requiredVersion: '^6.20.0' }\n }\n});\n\nconst Dashboard = lazy(() => loadRemote('dashboard/DashboardApp'));\nconst Checkout = lazy(() => loadRemote('checkout/CheckoutFlow'));\nconst Accounts = lazy(() => loadRemote('accounts/AccountsApp'));\nconst Settings = lazy(() => loadRemote('settings/SettingsApp'));\n\nfunction App() {\n return (\n <Shell>\n <Suspense fallback={<MFELoadingSkeleton />}>\n <Routes>\n <Route path=\"/dashboard/*\" element={<Dashboard />} />\n <Route path=\"/checkout/*\" element={<Checkout />} />\n <Route path=\"/accounts/*\" element={<Accounts />} />\n <Route path=\"/settings/*\" element={<Settings />} />\n </Routes>\n </Suspense>\n </Shell>\n );\n}\n```\n\nEl objeto ` window.__MFE_CONFIG__ ` lo inyecta el pipeline de deployment. En producción, apunta a URLs de CDN. En desarrollo, los desarrolladores pueden overridear entries específicas para apuntar a ` localhost:3001 `, ` localhost:3002 `, etc."},{"heading":"Import Maps como Capa de Discovery","level":2,"url":"https://lucioduran.com/es/blog/micro-frontends-module-federation-native-federation#import-maps-como-capa-de-discovery","content":"Acá encajan los import maps. En vez de hardcodear URLs de MFEs en el bundle JavaScript del host, servimos un import map desde un CDN que el host lee al arrancar:\n\n```html\n<script type=\"importmap\">\n{\n \"imports\": {\n \"dashboard-mfe\": \"https://cdn.example.com/mfe/dashboard/v2.14.0/\",\n \"checkout-mfe\": \"https://cdn.example.com/mfe/checkout/v3.2.1/\",\n \"accounts-mfe\": \"https://cdn.example.com/mfe/accounts/v1.8.0/\",\n \"settings-mfe\": \"https://cdn.example.com/mfe/settings/v2.0.0/\"\n }\n}\n</script>\n```\n\nEste import map lo genera el pipeline de CI cada vez que un MFE deployea. La ventaja crítica: es posible actualizar qué versión de un MFE se sirve sin redeployar la aplicación host. ¿Deployeaste checkout v3.2.2? Solo actualizá el import map. ¿Rollback? Actualizá el import map de nuevo. El host nunca cambia."},{"heading":"Estado Compartido: El Problema Difícil","level":2,"url":"https://lucioduran.com/es/blog/micro-frontends-module-federation-native-federation#estado-compartido-el-problema-difcil","content":"Acá es donde la mayoría de las arquitecturas micro-frontend se caen a pedazos. Cuatro equipos, siete MFEs, y todos necesitan saber quién es el usuario actual, cuáles son sus permisos, y si el carrito tiene items.\n\nLa tentación es compartir un store Redux. Esto es desaconsejable. Un store compartido crea acoplamiento implícito entre MFEs, el equipo A cambia un reducer, los selectors del equipo B se rompen, y nadie lo atrapa hasta producción porque deployean independientemente.\n\nEn su lugar, un enfoque de tres capas funciona bien:"},{"heading":"Capa 1: Contexto de Autenticación (Del Host)","level":3,"url":"https://lucioduran.com/es/blog/micro-frontends-module-federation-native-federation#capa-1-contexto-de-autenticacin-del-host","content":"El host es dueño del estado de autenticación y lo expone via un event bus custom:\n\n```javascript\n// host/src/auth-bus.js\nclass AuthBus {\n #listeners = new Set();\n #currentUser = null;\n\n setUser(user) {\n this.#currentUser = Object.freeze(user);\n this.#notify('user-changed', this.#currentUser);\n }\n\n getUser() { return this.#currentUser; }\n\n subscribe(callback) {\n this.#listeners.add(callback);\n if (this.#currentUser) {\n callback({ type: 'user-changed', payload: this.#currentUser });\n }\n return () => this.#listeners.delete(callback);\n }\n\n #notify(type, payload) {\n const event = Object.freeze({ type, payload });\n this.#listeners.forEach(cb => cb(event));\n }\n}\n\nwindow.__AUTH_BUS__ = new AuthBus();\n```"},{"heading":"Capa 2: Eventos Cross-MFE (BroadcastChannel)","level":3,"url":"https://lucioduran.com/es/blog/micro-frontends-module-federation-native-federation#capa-2-eventos-cross-mfe-broadcastchannel","content":"Para eventos que cualquier MFE puede producir y cualquier otro consumir (como \"item agregado al carrito\"), usamos ` BroadcastChannel `:\n\n```javascript\n// shared-lib/src/mfe-events.js\nconst channel = new BroadcastChannel('mfe-events');\n\nexport function emitMFEEvent(type, payload, source) {\n channel.postMessage({ type, payload, source, timestamp: Date.now() });\n}\n\nexport function onMFEEvent(type, callback) {\n const handler = (event) => {\n if (event.data.type === type) callback(event.data);\n };\n channel.addEventListener('message', handler);\n return () => channel.removeEventListener('message', handler);\n}\n```"},{"heading":"Capa 3: URL como Estado (Router-Driven)","level":3,"url":"https://lucioduran.com/es/blog/micro-frontends-module-federation-native-federation#capa-3-url-como-estado-router-driven","content":"El estado de navegación compartido vive en la URL. Si el dashboard necesita linkear a una cuenta específica, navega a ` /accounts/ACC-123 `. El MFE de accounts lee sus route params. Sin estado compartido necesario."},{"heading":"La Migración: Patrón Strangler Fig","level":2,"url":"https://lucioduran.com/es/blog/micro-frontends-module-federation-native-federation#la-migracin-patrn-strangler-fig","content":"El monolito no se reescribió. Se estranguló. Acá va un timeline representativo:\n\n**Mes 1-2**: Levantar el host shell, infraestructura de Module Federation, y pipeline de CI/CD. El host renderizaba el monolito entero como un solo remote \"legacy\".\n\n**Mes 3-4**: Se extrajo la página de settings (menor riesgo, menos dependencias) a su propio MFE. Dos ingenieros, dos semanas de código real. El resto fue plomería de CI/CD.\n\n**Mes 5-6**: Se extrajo checkout (mayor valor de negocio, más complejo). Esto requirió la arquitectura de estado compartido descrita arriba. Tres ingenieros, seis semanas.\n\n**Mes 7-10**: Se extrajeron dashboard y accounts en paralelo. A esa altura, los patrones estaban establecidos y cada extracción tomó 3-4 semanas por equipo.\n\n**Mes 11-12**: Se decomisionó el remote del monolito legacy. Lo que quedó fue el host shell + cuatro MFEs independientes."},{"heading":"Errores Comunes","level":3,"url":"https://lucioduran.com/es/blog/micro-frontends-module-federation-native-federation#errores-comunes","content":"1. **Aislamiento CSS.** Sin aislamiento CSS enforceado desde el día uno, los estilos globales del MFE A se filtran al MFE B. La solución: Shadow DOM para MFEs nuevos y un plugin PostCSS que scopea todos los selectores para código legacy.\n2. **Drift de versiones de dependencias compartidas.** En tres meses, dos MFEs estaban en React 18.3.1 y dos en 18.2.0. El singleton mode de Module Federation hizo que los hooks de un equipo fallaran silenciosamente. Agregar un check de CI que falle si las versiones de dependencias compartidas divergen más de un minor previene esto.\n3. **Error boundaries.** Cuando un MFE remoto falla al cargar (caída de CDN, problema de red), todo el host crasheaba porque no wrapeamos ` loadRemote() ` en error boundaries apropiados. Ahora cada punto de montaje de MFE tiene un ` <MFEErrorBoundary> ` que muestra una UI de retry."},{"heading":"Impacto en Performance","level":2,"url":"https://lucioduran.com/es/blog/micro-frontends-module-federation-native-federation#impacto-en-performance","content":"Métricas clave de performance:\n\n| Métrica | Monolito | Micro-Frontends | Delta |\n| --- | --- | --- | --- |\n| Bundle JS inicial | 2.4MB (gzipped 680KB) | 320KB host + MFEs lazy | -53% carga inicial |\n| Time to Interactive | 4.2s | 1.8s (host) + 0.6s (primer MFE) | -43% |\n| Tiempo de deploy | 22 min (build + E2E completo) | 3-4 min por MFE | -82% |\n| Frecuencia de deploy | 2-3/día (todos los equipos combinados) | 3-5/día por equipo | +400% |\n\nLa carga inicial mejoró porque los usuarios solo descargan el código de la página que visitan. Los 1.8MB de librerías de charting del equipo de dashboard no se descargan hasta que alguien visita ` /dashboard `.\n\nEl tradeoff: hay un breve estado de loading al navegar entre MFEs por primera vez mientras el bundle remoto se descarga. Precargar el MFE más probable siguiente basándose en patrones de navegación del usuario, lo que mitiga esto para el 80% de las transiciones."},{"heading":"Cuándo Tienen Sentido los Micro-Frontends","level":2,"url":"https://lucioduran.com/es/blog/micro-frontends-module-federation-native-federation#cundo-tienen-sentido-los-micro-frontends","content":"Los micro-frontends se justifican solo para organizaciones con la estructura de equipo que lo demande. Los micro-frontends son una solución organizacional a un problema organizacional. Con un equipo, un monolito es lo indicado. Con dos equipos, un monorepo con code ownership. Con cuatro o más equipos que necesitan cadencias de deploy independientes, los micro-frontends valen la complejidad.\n\nLa tecnología finalmente está lo suficientemente madura como para que el dolor de implementación sea manejable. La guía: arrancar con un monolito. Extraer cuando el dolor organizacional lo demande. Y al extraer, usar el patrón strangler fig. Es el único approach de migración que te deja shipear features a los usuarios cada semana mientras la descomposición pasa en el fondo."},{"heading":"Preguntas frecuentes","level":2,"url":"https://lucioduran.com/es/blog/micro-frontends-module-federation-native-federation#questions","content":""},{"heading":"¿Cuál es la diferencia entre Module Federation y Native Federation?","level":3,"url":"https://lucioduran.com/es/blog/micro-frontends-module-federation-native-federation#question-1","content":"Module Federation es un plugin de webpack/rspack que habilita compartir módulos en runtime entre aplicaciones buildeadas separadamente. Native Federation logra lo mismo pero usando ES modules nativos del navegador e import maps en vez de un runtime específico del bundler, haciéndolo agnóstico de framework y bundler."},{"heading":"¿Debería usar micro-frontends para mi proyecto?","level":3,"url":"https://lucioduran.com/es/blog/micro-frontends-module-federation-native-federation#question-2","content":"Solo si teners múltiples equipos que necesitan deployar cambios de frontend independientemente. Para un solo equipo o una aplicación chica, los micro-frontends agregan complejidad significativa con beneficio mínimo. El punto óptimo es 3+ equipos, 50k+ líneas de código frontend, y una cadencia de deploy que varía por equipo."},{"heading":"¿Cómo manejan los micro-frontends dependencias compartidas como React?","level":3,"url":"https://lucioduran.com/es/blog/micro-frontends-module-federation-native-federation#question-3","content":"Module Federation 2.0 lo maneja via su configuración shared, que negocia versiones compatibles en runtime. Native Federation usa import maps para apuntar todos los micro-frontends a la misma URL de dependencia singleton. Ambos enfoques funcionan pero tienen modos de falla diferentes cuando las versiones divergen."},{"heading":"Tips","level":2,"url":"https://lucioduran.com/es/blog/micro-frontends-module-federation-native-federation#article-tips","content":"1. **Tip:** Nunca compartas React via la config ` shared ` de Module Federation con ` singleton: true ` a menos que TODOS tus remotes estén en la misma versión exacta de React. Un mismatch de versión con singleton mode causa fallas silenciosas de hooks que son casi imposibles de debuggear en producción.\n2. **Tip:** Utilizar import maps como tu capa de discovery de micro-frontends aunque uses Module Federation para la carga. Overrideá el import map en staging para apuntar remotes específicos a localhost para el loop de desarrollo más rápido posible.\n3. **Tip:** Para estado compartido entre micro-frontends, utilizar la API ` BroadcastChannel ` del navegador para loose coupling en vez de un store Redux compartido. Cada MFE es dueño de su propio estado, la comunicación cross-MFE pasa por eventos, no por memoria compartida.\n\n- micro-frontends\n- module-federation\n- native-federation\n- import-maps\n- webpack\n- rspack\n- estado-compartido\n- arquitectura-frontend\n\n[Volver al Blog](<https://lucioduran.com/es/blog>)"}],"links":[{"text":"English","url":"https://lucioduran.com/blog/micro-frontends-module-federation-native-federation"},{"text":"Module Federation","url":"https://module-federation.io/"},{"text":"Volver al Blog","url":"https://lucioduran.com/es/blog"}],"structuredData":[{"@context":"https://schema.org","@graph":[{"@type":"BlogPosting","@id":"https://lucioduran.com/es/blog/micro-frontends-module-federation-native-federation#article","mainEntityOfPage":{"@type":"WebPage","@id":"https://lucioduran.com/es/blog/micro-frontends-module-federation-native-federation"},"headline":"Micro-Frontends en 2026: Module Federation 2.0, Native Federation e Import Maps","description":"Guía exhaustiva de arquitectura micro-frontend en 2026, Module Federation 2.0, Native Federation, import maps, manejo de estado compartido, y una migración documentada de monolito SPA a frontends deployables independientemente.","datePublished":"2025-11-02","author":{"@type":"Person","@id":"https://lucioduran.com/#person","name":"Lucio Durán","url":"https://lucioduran.com"},"publisher":{"@type":"Person","@id":"https://lucioduran.com/#person","name":"Lucio Durán","url":"https://lucioduran.com"},"image":"https://statics.lucioduran.com/images/lucioduran-social-card.jpg","url":"https://lucioduran.com/es/blog/micro-frontends-module-federation-native-federation","inLanguage":"es","isPartOf":{"@id":"https://lucioduran.com/blog#blog"},"articleSection":"Frontend","keywords":"micro-frontends, module-federation, native-federation, import-maps, webpack, rspack, estado-compartido, arquitectura-frontend","wordCount":1420,"timeRequired":"PT8M","workTranslation":[{"@type":"BlogPosting","inLanguage":"en","url":"https://lucioduran.com/blog/micro-frontends-module-federation-native-federation"}]},{"@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"Inicio","item":"https://lucioduran.com/"},{"@type":"ListItem","position":2,"name":"Blog","item":"https://lucioduran.com/es/blog"},{"@type":"ListItem","position":3,"name":"Micro-Frontends en 2026: Module Federation 2.0, Native Federation e Import Maps","item":"https://lucioduran.com/es/blog/micro-frontends-module-federation-native-federation"}]},{"@type":"FAQPage","mainEntity":[{"@type":"Question","name":"¿Cuál es la diferencia entre Module Federation y Native Federation?","acceptedAnswer":{"@type":"Answer","text":"Module Federation es un plugin de webpack/rspack que habilita compartir módulos en runtime entre aplicaciones buildeadas separadamente. Native Federation logra lo mismo pero usando ES modules nativos del navegador e import maps en vez de un runtime específico del bundler, haciéndolo agnóstico de framework y bundler."}},{"@type":"Question","name":"¿Debería usar micro-frontends para mi proyecto?","acceptedAnswer":{"@type":"Answer","text":"Solo si teners múltiples equipos que necesitan deployar cambios de frontend independientemente. Para un solo equipo o una aplicación chica, los micro-frontends agregan complejidad significativa con beneficio mínimo. El punto óptimo es 3+ equipos, 50k+ líneas de código frontend, y una cadencia de deploy que varía por equipo."}},{"@type":"Question","name":"¿Cómo manejan los micro-frontends dependencias compartidas como React?","acceptedAnswer":{"@type":"Answer","text":"Module Federation 2.0 lo maneja via su configuración shared, que negocia versiones compatibles en runtime. Native Federation usa import maps para apuntar todos los micro-frontends a la misma URL de dependencia singleton. Ambos enfoques funcionan pero tienen modos de falla diferentes cuando las versiones divergen."}}]}]}],"contentHash":"1ea8d7ad3978833723606947c964cf8a70c180fbd8bde5640be4d572de3db83f","provenance":{"method":"Deterministic serialization of the server-rendered main element","source":"https://lucioduran.com/es/blog/micro-frontends-module-federation-native-federation","hashAlgorithm":"sha256","hashScope":"JSON document fields before contentHash and provenance","sourceHTMLBytes":70185,"contentBytes":13420}}
