Logo

Introducción práctica a Microfrontends con Rspack y Module Federation

Cuando nos referimos a microfrontends, básicamente estamos hablando de una arquitectura basada en componentes. Su principal objetivo es ofrecer flexibilidad y escalabilidad en el desarrollo de nuestras aplicaciones, permitiendo que cada componente sea independiente y pueda ser desarrollado por distintos equipos.

En frontend, cuando nos referimos a componentes, estamos aludiendo a las funcionalidades que podría ver el usuario, ya sea que estén integradas en una misma vista o separadas. Para nuestro caso de uso, nos concentraremos en una sola vista que estará compuesta por un header desarrollado en React y el cuerpo principal desarrollado en Vue.

Recuerden, la idea de usar distintos frameworks es ejemplificar el desarrollo por equipos. Es decir, haremos la suposición de que el header desarrollado en React pertenece a un equipo totalmente distinto al que desarrolla el cuerpo principal en Vue.

Hagamos un repaso rápido de algunas arquitecturas que podemos encontrar hoy en día.

Monolito

Es un desarrollo en el que toda la lógica (backend, frontend, bases de datos, etc.) se encuentra en un mismo código base o proyecto.

Microservicios

Ya estamos hablando de una separación de responsabilidades. En este caso, el proyecto de backend se encarga de la lógica de negocio y el proyecto de frontend se encarga de la interfaz de usuario. Incluso pueden existir servicios dedicados a un API Gateway o a la base de datos.

Microfrontends

Mantiene la separación de responsabilidades, pero en el caso de frontend, la separación se hace por componentes o características específicas que vería el usuario. Cada una de ellas sería un proyecto separado, con su propio código y despliegue independiente.

Estructura del proyecto

Vamos a estructurar nuestro proyecto en tres carpetas: container, header y body. container será nuestro anfitrión y el resto serán componentes remotos.

module-federation/ │── container/ (host) │── header/ (remote) │── body/ (remote)

Para efectos prácticos, usaremos Rspack para cada uno de los componentes que iremos configurando.

Configuración de header (react)

Instalamos Rspack con pnpm o el gestor de paquetes de su preferencia:

cd module-federation
pnpm create rspack@latest

Cuando nos pregunte el nombre del proyecto, escribimos header y después seleccionaremos React. El resto de opciones no serán necesarias.

Seguido de esto, instalaremos @module-federation/enhanced, que será el encargado de la comunicación con el anfitrión.

cd header
pnpm add @rspack/core @rspack/cli @module-federation/enhanced -D

Una vez que tengamos todo instalado, procederemos a configurar Module Federation. Buscamos el archivo rspack.config.js y añadimos la siguiente configuración a los plugins:

// rspack.config.js

import { ModuleFederationPlugin } from '@module-federation/enhanced/rspack';

// ...

plugins: [
  // ...
  new ModuleFederationPlugin({
    name: 'header_app',
    filename: 'remoteEntry.js',
    exposes: {
      './index': './src/main.tsx',
    },
  }),
],

Básicamente, estamos diciendo que el nombre del proyecto es header_app, el archivo generado es remoteEntry.js y que va a exponer ./index, que hace referencia a ./src/main.ts, el archivo de entrada de nuestro proyecto.

Como esto es React, debemos tener en cuenta que debe haber un elemento base en el DOM para poder renderizar el componente. Normalmente, este elemento tiene el id root, pero para nuestro caso, le daremos el id header:

<!-- index.html -->
<div id="header"></div>

Y finalmente, en el archivo main.tsx, cambiamos el id a header dentro de getElementById:

// src/main.tsx

// ...

ReactDOM.createRoot(document.getElementById('header') as HTMLElement).render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
);

Listo, ya tenemos configurado el proyecto header, el resto ya sería maquetar y estilizar nuestro componente.

Configuración de body (vue)

Haremos los primeros pasos de instalación exactamente igual que para el header en React. La diferencia es que, cuando nos pregunten por el nombre, escribimos body y seleccionamos el framework a usar, que será Vue.

cd ..
pnpm create rspack@latest

Una vez que tengamos todo instalado, procederemos a configurar Module Federation. Buscamos el archivo rspack.config.js, y la configuración será la misma que en React, con la diferencia de que en Vue el archivo de entrada no tiene la extensión .tsx, sino .ts.

// rspack.config.js

import { ModuleFederationPlugin } from '@module-federation/enhanced/rspack';

// ...

plugins: [
  // ...
  new ModuleFederationPlugin({
    name: 'header_app',
    filename: 'remoteEntry.js',
    exposes: {
      './index': './src/main.ts',
    },
  }),
],

Al igual que en React, debemos tener en cuenta que debe haber un elemento base en el DOM para poder renderizar el componente. En Vue, el id por defecto es app, pero vamos a cambiarlo a body:

<!-- index.html -->
<div id="body"></div>

Y finalmente, en el archivo main.ts, cambiamos el id a body dentro de createApp:

// src/main.ts

// ...

createApp(App).mount('#body');

Listo, ya tenemos configurado el proyecto body. El siguiente paso es maquetar y estilizar nuestro componente.

Configuración de container (vanilla js)

Al igual que en nuestros componentes remotos, debemos crear el proyecto para el anfitrión. En este caso, utilizaremos vanilla js para crear nuestro proyecto.

Cuando nos pregunten por el nombre, escribimos container y seleccionamos el framework a usar, que sería vanilla.

cd ..
pnpm create rspack@latest

Una vez que tengamos todo instalado, procederemos a configurar Module Federation. Buscamos el archivo rspack.config.js y la configuración será la siguiente:

// rspack.config.js

import { ModuleFederationPlugin } from '@module-federation/enhanced/rspack';

// ...

plugins: [
  // ...
  new ModuleFederationPlugin({
    name: 'container_host',
    remotes: {
      header_app: 'header_app@http://localhost:8080/remoteEntry.js',
      body_app: 'body_app@http://localhost:8081/remoteEntry.js',
    },
  }),
],

Con esto, queremos indicarle a Module Federation que se comunique con los proyectos remotos con los que queremos trabajar. Es importante que el puerto sea el correcto para cada proyecto; en nuestro caso, sería 8080 para el header y 8081 para el body. Podemos utilizar el puerto que prefiramos, y lo podremos editar en el archivo de configuración rspack.config.ts.

// rspack.config.js

devServer: {
  port: <port number>
},

El siguiente paso es modificar el archivo index.html para crear los elementos base en el DOM tanto para header como para body. Estos deben tener el mismo id que configuramos anteriormente:

<!-- index.html -->
<body>
  <div id="header"></div>
  <div id="body"></div>
</body>

Recuerden que, en este punto, la maquetación y estilización de nuestro contenedor es totalmente flexible; la única restricción son los nombres de los IDs en los elementos base para poder renderizarlos.

Finalmente, nos queda modificar el archivo de entrada en nuestro contenedor, src/index.ts:

// src/index.ts

document.addEventListener('DOMContentLoaded', async () => {
  try {
    await import('header_app/index');
    await import('body_app/index');
  } catch (error) {
    // Handle error
  }
});

Con esto, queremos que se carguen los módulos de ambos proyectos una vez que los elementos del DOM hayan cargado (DOMContentLoaded).

Y eso es todo. Solo nos queda probar que todo funcione: ejecutamos el script dev de los proyectos remotos y, finalmente, el del anfitrión.

Conclusiones

Esta es la manera más básica de conectar microfrontends con Module Federation. Recuerden que los tres proyectos pueden actuar como anfitrión y remoto al mismo tiempo; no es necesario que solo el anfitrión pueda traer otros módulos externos; cualquier componente que lo requiera puede hacerlo. La clave está en sus identificadores y en los elementos base donde se alojarán.

En resumen, Module Federation nos permite conectar proyectos de diferentes tecnologías y frameworks, permitiendo así una arquitectura flexible y escalable en el desarrollo de nuestras aplicaciones.


webmicrofrontendsarquitecturarspackreactvuemodule-federation