Microfrontends con Module Federation
Tags: modulefederation, react, microfrontends
Published: 2023-04-09 12:37:02

En los últimos años, las aplicaciones web han evolucionado hacia arquitecturas más complejas y modulares, lo que ha llevado a la aparición de nuevos conceptos y tecnologías que facilitan el desarrollo y mantenimiento de dichas aplicaciones. En este contexto, surge el concepto de **microfrontends** y la técnica conocida como *Module Federation*.

A continuación, se presenta el concepto de microfrontends, se describe brevemente *Module Federation* y se establece la importancia de combinar ambos enfoques para mejorar la calidad y escalabilidad de las aplicaciones web modernas.

Los microfrontends son un enfoque arquitectónico que busca dividir una aplicación web en partes más pequeñas e independientes, llamadas micro-app, permitiendo que cada parte sea desarrollada, probada y desplegada de forma autónoma.  
  
Esta técnica se inspira en la arquitectura de microservicios, ampliamente utilizada en el desarrollo de aplicaciones backend, y busca replicar sus ventajas en el ámbito del frontend.

Ambos conceptos, microfrontends y microservicios, están estrechamente relacionados ya que buscan abordar problemas similares en sus respectivas capas de aplicación: mejorar la escalabilidad, la mantenibilidad y la flexibilidad.

Algunos de los beneficios de utilizar microfrontends incluyen la mejora en la escalabilidad, la facilidad de mantenimiento y la posibilidad de emplear diferentes tecnologías en cada micro-app del frontend.

## Module Federation

**Module Federation** es una característica introducida en Webpack 5 que permite a múltiples aplicaciones JavaScript compartir dependencias y módulos de código en tiempo de ejecución.  
Esto significa que las aplicaciones pueden cargar módulos de otras aplicaciones de forma dinámica, sin la necesidad de incluirlos en su propio código fuente.

Esta técnica permite que diferentes equipos de desarrollo colaboren de manera más eficiente y ahorren recursos al evitar la duplicación de código en aplicaciones separadas.

La combinación de microfrontends con *Module Federation* ofrece una serie de ventajas significativas para el desarrollo de aplicaciones web modernas.  
  
Al utilizar microfrontends, se facilita la distribución del trabajo entre diferentes equipos, permitiendo que cada uno se enfoque en su respectiva parte de la aplicación.  
Además, el uso de *Module Federation* permite compartir y reutilizar módulos de código entre los distintos microfrontends, lo que optimiza el rendimiento y reduce la duplicidad de código.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1680862050174/4f7ea4c7-edfe-466b-8d5f-8ce5b618f93a.png align="center")

### Beneficios Microfrontend

**Escalabilidad:** Dividir la aplicación de front-end en componentes más pequeños permite a los equipos trabajar de manera más eficiente y escalar sus esfuerzos de desarrollo. Esto también permite a las organizaciones asignar recursos de manera más eficiente a diferentes partes de la aplicación, en función de las necesidades y prioridades del negocio.

**Mantenibilidad:** Los componentes de un microfrontend son más fáciles de mantener, ya que cada uno es autónomo y tiene su propio conjunto de responsabilidades. Esto facilita la actualización, la corrección de errores y la mejora de cada componente sin afectar a otros componentes o al sistema en general.

**Desacoplamiento:** Los microfrontends promueven un enfoque de desarrollo desacoplado, en el que cada componente puede evolucionar de forma independiente y a su propio ritmo. Esto permite a los equipos de desarrollo adoptar diferentes tecnologías y prácticas sin afectar a otros equipos o componentes.

**Facilita la colaboración entre equipos:** Al dividir el front-end en componentes más pequeños, diferentes equipos pueden trabajar de forma independiente en diferentes partes de la aplicación. Esto reduce la necesidad de coordinación y comunicación entre equipos, lo que puede agilizar el proceso de desarrollo y mejorar la productividad.

**Reutilización de componentes:** Los microfrontends permiten la creación de componentes reutilizables que pueden ser compartidos entre diferentes partes de la aplicación o incluso entre diferentes aplicaciones. Esto puede reducir la duplicación de esfuerzos y mejorar la coherencia en la experiencia del usuario.

**Mejora la resiliencia:** Al aislar los componentes y evitar la interdependencia entre ellos, los microfrontends pueden mejorar la resiliencia de la aplicación. Si un componente falla, es menos probable que afecte a otros componentes o a la aplicación en su conjunto.

Antes de pasar a la implementación técnica, debemos comprender algunos conceptos básicos para entender como funciona *module federation*.  
  
*Module federation* permite que una aplicación JavaScript ejecute código de forma dinámica desde otro paquete (aplicación) o se construya tanto en el cliente como en el servidor.

*Module federation* proporciona dos conceptos clave que debemos comprender antes de trabajar con ellas.

  
**Host**: El contenedor encargado de cargar, micro-frontends o componentes en tiempo de ejecución desde otras aplicaciones.  
El *host*, también puede ser conocido, como *container* o *shell*

**Remote**: El *bundle* de javascript que queremos cargar dentro de un host.

### Tu primer Microfrontend

Voy a utilizar un *package* de npm llamado [`create-mf-app`](https://www.npmjs.com/package/create-mf-app), realmente podríamos hacerlo manualmente.  
De hecho si ya tienes tus aplicaciones creadas solo debes añadir el fichero *webpack.config.js* en cada una de tu aplicación.

Ejecuta este comando

```bash
npx i create-mf-app
```

A partir de ahora en la terminal, verás varias opciones.  
La primera es el nombre de nuestra aplicación, yo le he llamado ***container***

```bash
? Pick the name of your app: (host)
```

El tipo de nuestra aplicación, aquí tenemos tres opciones. *Application, API Server y LIbrary.*  
Vamos a escoger la primera opción\*.\* ***Application***

```bash
? Project Type: (Use arrow keys)
> Application
  API Server
  Library
```

Ahora es el turno del puerto, vamos a elegir el puerto ***4000***

```bash
? Port number: (8080)
```

Para este ejemplo, yo voy a utilizar dos aplicaciones de React, anque recuerda que puedes usar varios frameworks.

```bash
? Framework: (Use arrow keys)
  lit-html
  mithril
  preact
> react
  solid-js
  svelte
  vanilla
```

Yo, voy a escoger ***TypeScript*** type safe 👨‍💻💪

```bash
? Language:
> typescript
  javascript
```

Los estilos para esta demo no tienen importancia, pero yo escogí ***Tailwind***

```bash
`? CSS: (Use arrow keys)
  CSS
> Tailwind
```

Y finalmente ya tendremos nuestra aplicación, los siguentes pasos serán acceder a la carperta y ejecutar `npm install && npm start`

```bash
Your 'host' project is ready to go.

Next steps:

▶️ cd container
▶️ npm install
▶️ npm start
```

Bien, para la creación de la segunda aplicación los pasos serán los mismos, pero con otro nombre obviamente. En mi caso le he llamado ***remote.***  
En tu editor de codigo favorito, abre el proyecto.

Y nos centraremos en el ***webpack.config.js***, y principalmente en el apartado de plugins, en el *plugin* **ModuleFederationPlugin**.  
Podremos ver varias propiedades que utiliza webpack, para configurar el comportamiento de nuestra aplicación.

```javascript
  plugins: [
    new ModuleFederationPlugin({
      name: "container",
      filename: "remoteEntry.js",
      remotes: {},
      exposes: {},
      shared: {
        ...deps,
        react: {
          singleton: true,
          requiredVersion: deps.react,
        },
        "react-dom": {
          singleton: true,
          requiredVersion: deps["react-dom"],
        },
      },
    }),
    new HtmlWebPackPlugin({
      template: "./src/index.html",
    }),
  ],
```

**ModuleFederationPlugin**, es un plugin de webpack que permite compartir módulos entre diferentes aplicaciones y proyectos en tiempo de ejecución, sin tener que empaquetarlos en cada proyecto.

El plugin facilita la integración de varias aplicaciones en una sola página, lo que mejora el rendimiento y la experiencia del usuario.

En la configuración del plugin, se establece el nombre de la aplicación actual como container, *(name)* el nombre del archivo de entrada remota como ***remoteEntry.js***, y se definen las propiedades ***remotes***, ***exposes*** y ***shared***.

La propiedad ***remotes*** se utiliza para especificar los módulos que se importarán de forma remota. En este caso, se establece como un objeto vacío, lo que significa que no hay módulos remotos.

La propiedad ***exposes*** se utiliza para definir los módulos que se exponen a otros proyectos. En este caso, se establece como un objeto vacío, lo que significa que no se están exponiendo módulos.  

Vamos a crear un componente de React en nuestra aplicación ***remote***

```typescript
// Header.tsx
import React, { FC } from 'react';

interface HeaderProps {}

const Header: FC<HeaderProps> = () => {
  return <div className='p-5 bg-blue-600 text-white text-3xl'>Header V1</div>;
};

export default Header;
```

Ahora lo voy añadir a mi ***App.tsx*** (aunque realmente esto no es importante para el ejemplo)  
Ahora nuestra aplicación remote debe lucir algo como esto.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1680882324209/64f93a21-735b-439b-bf45-fa6144d72c8e.png align="center")

Great 🙂

Ahora que tenemos nuestro componente Header lo vamos a exponer.  
Volvamos al fichero ***webpack.config.js*** y en la propiedad ***exposes*** añadimos lo siguiente.  

```javascript
      exposes: {
        './Header': './src/Header.tsx',
      }
```

Con esto estamos exponiendo nuestro header para que pueda ser consumido por otras aplicaciones.

Para comprobar que se está sirviendo de la manera correcta, podemos acceder a la siguiente url [*http://localhost:4001/remoteEntry.js*](http://localhost:4001/remoteEntry.js)  
**Asegurate de reiniciar el la aplicación**

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1680893338479/cc17ed48-cf66-4415-ade4-98cc0f1e4bc5.png align="center")

Lo siguiente, que haremos es consumir el *Header* expuesto por el *remote.*

Volvamos al fichero ***webpack.config.js*** y en la propiedad ***remotes*** pero del **container.**

```javascript
  plugins: [
    new ModuleFederationPlugin({
      name: "container",
      filename: "remoteEntry.js",
      remotes: {
        'components': 'remote@http://localhost:4001/remoteEntry.js',
      },
      exposes: {},
      shared: {
        ...deps,
        react: {
          singleton: true,
          requiredVersion: deps.react,
        },
        "react-dom": {
          singleton: true,
          requiredVersion: deps["react-dom"],
        },
      },
    }),
    new HtmlWebPackPlugin({
      template: "./src/index.html",
    }),
  ],
```

Ahora estamos listos para utilizar nuestro componentes remote.

```typescript
// App.tsx  
import React from 'react';
import ReactDOM from 'react-dom';

import './index.scss';
import Header from 'components/Header';

const App = () => (
  <>
    <Header />
    <div className='max-w-6xl mx-auto mt-10 text-3xl'>
      <div>Name: container</div>
      <div>Framework: react</div>
      <div>Language: TypeScript</div>
      <div>CSS: Tailwind</div>
    </div>
  </>
);
ReactDOM.render(<App />, document.getElementById('app'));
```

Modificamos la aplicación container, el componente ***App.tsx***

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1680973639230/acf14603-1e78-4b78-87e9-1a9d5c32bf57.png align="center")

Deberías ver algo como esto en tu aplicación container.

Esto solo es un pequeña muestra de lo que podríamos lograr con Webpack 5 y *Module federation.*

[Aquí tienes el reposito de la demo](https://github.com/domini-code/module-federation-react)