Cinco squads en el mismo repositorio frontend producen conflicto de merge diario, un build que crece en cada sprint y un release trabado por un equipo que no toca tu feature. Micro-frontends separan la aplicación en unidades que cada squad publica solo. El intercambio aparece después de la adopción: runtime, deploy y debug ganan piezas nuevas.

El problema que ataca la arquitectura

Los micro-frontends se pagan con tres condiciones simultáneas: 3+ equipos en el mismo frontend, release independiente por squad y poca superposición de código entre ellos. Cam Jackson, en el artículo Micro Frontends (martinfowler.com, 2019), plantea el objetivo como aumentar la efectividad de los equipos que trabajan en el mismo producto, y el costo aparece en mantener coherentes las piezas que publican a velocidades distintas.

Un squad de dos equipos con frontera de dominio clara paga la infraestructura de micro-frontends y no recibe velocidad de vuelta. Mide el tiempo entre abrir el PR y llegar a producción antes de comprometerte con cualquier migración.

Tres formas de componer la página

Build-time integration: Module Federation

El Module Federation de Webpack carga módulos de otra aplicación en runtime. Cada micro-frontend es una aplicación standalone que exporta un remote entry, y el shell decide cuál entra en cada ruta. Rspack y el proyecto independiente de Module Federation mantienen el mismo concepto.

// webpack.config.js: shell
new ModuleFederationPlugin({
  name: 'shell',
  remotes: {
    checkout: 'checkout@https://checkout.example.com/remoteEntry.js',
    catalog: 'catalog@https://catalog.example.com/remoteEntry.js',
  },
})

Integración en runtime con iframe

El iframe aísla estilos, JavaScript y ciclo de vida por completo. La comunicación se vuelve postMessage, y el costo aparece en accesibilidad, SEO y compartir sesión entre las piezas.

Composición en el servidor: SSI y ESI

El servidor arma la página antes de enviarla y el cliente recibe HTML listo. La orquestación sale del browser y el requisito pasa a ser infraestructura de include más un contrato de HTML claro entre equipos.

Estructura de repositorios en la práctica

shell-app/              # Arma la página y el layout base
├── src/
│   ├── Header.tsx      # Compartido entre todos
│   ├── Footer.tsx      # Compartido entre todos
│   └── MicroApp.tsx    # Loader de los micro-frontends
│
checkout/               # Squad de checkout
├── src/
│   ├── Checkout.tsx
│   ├── Payment.tsx
│   └── Shipping.tsx
│
catalog/                # Squad de catálogo
├── src/
│   ├── ProductList.tsx
│   └── ProductDetail.tsx

Costos que aparecen en operaciones

  • Comunicación: el dato compartido exige event bus, store en el shell o contrato de props. Sin eso, cada squad guarda su propia copia del carrito.
  • Estilo: un design system en paquete versionado es prerrequisito. Tokens y componentes base salen de un solo lugar.
  • Performance: cada bundle extra cuesta requests HTTP y parseo de JS. El shell cuida el preload y el lazy load por ruta.
  • Debug: un stack trace que cruza dos bundles pide dos equipos en la misma call.
  • Deploy: la dependencia entre squads devuelve el deploy coordinado que la arquitetura prometía eliminar.

Los equipos que llegan al quinto costo descubren que la independencia de deploy sobrevive mientras los contratos entre las piezas sigan estables.

Cuándo adoptar

Adopta cuando tres o más squads publican en el mismo producto sin coordinación de release, el cuello de botella de merge ya aparece en una métrica y cada equipo puede explicar la frontera de dominio de su propia porción. La velocidad del equipo gana espacio; el costo de infraestructura entra al presupuesto de todos modos.

Cuándo quedarse en el monolito

Dos equipos o menos, features superpuestas y release compartido piden un monolito modular con carpetas y módulos por dominio. Una arquitectura nueva no repara un proceso de equipo roto, y el monolito bien dividido publica igual de rápido.

Referencias