Iniciando com Micro front-end (module federation)
Tags: javascript, webdev, frontend, webpack
Published: 2023-03-23 15:25:49

Hoje em dia existem várias formas de se construir aplicações front end distribuídas. Também existem inúmeros frameworks para facilitar a vida de quem está construindo um ambiente de componentes distribuídos e diferentes formas de gerenciar dependências e orquestrar os componentes visíveis no browser.

Neste artigo vou utilizar o "_module federation_", um modelo arquitetural proposto por Zack Jackson e outros _Core Developers_ do Webpack. Como o module federation é uma especificação, existem implementações em diferentes Module bundlers como o Rollup. Porém a que está com a api mais avançada é mesmo o Webpack. Então os exemplos desse artigo são escritos utilizando do plugin ModuleFederation do Webpack.

A forma mais fácil de iniciar um projeto com suporte ao module federation é através da cli `create-mf-app` desenvolvida pelo Jack Herrington (o canal do youtube dele tem bastante coisa sobre micro front-end lá, vale a pena dar uma olhada [https://www.youtube.com/c/jackherrington](https://www.youtube.com/c/jackherrington) ).

Para utilizar a cli basta executar o comando

```
npx create-mf-app
```

Siga as instruções da cli. Ela vai te perguntar algumas informações como nome da app, framework, porta para localhost e etc.

```
? Pick the name of your app: host
? Project Type: Application
? Port number: 8080
? Framework: vanilla
? Language: javascript
? CSS: CSS
Your 'host' project is ready to go.

Next steps:

▶️ cd host
▶️ npm install
▶️ npm start
```

Após executar esses passos podemos avaliar alguns arquivos importantes.

Veja o arquivo `webpack.config.js` é nele que vai estar as configurações do nosso micro front-end. Vá a propriedade de plugins desse arquivo. Lá você deve ver a configuração de um plugin chamado `ModuleFederationPlugin` e ele deve ser parecido com isso:

```javascript

//...

plugins: [
  //...

  new ModuleFederationPlugin({
    name: "host",
    filename: "remoteEntry.js",
    remotes: {},
    exposes: {},
    shared: {
      ...require("./package.json").dependencies,
    },
  }),

  //...
]

```
Nesse plugin é onde vamos fazer toda a configuração e federação dos nossos módulos remotos (micro front-ends).

> Dentro da literatura do Module Federation eles chamam os micro front-ends de Remote modules (Módulos remotos). E a partir daqui, eu também vou utilizar essa nomenclatura de módulo ou módulo remoto.

Uma grande vantagem da estratégia do Module Federation é que os módulos remotos são verdadeiramente módulos nativos do javascript. Módulos que você utiliza normalmente com `import pack from package` ou com `require('package')`. Diferente de outras estratégias de micro front-end como o Single-SPA que utilizam um pacote como dependência para garantir a integração entre os componentes, o Module Federation utiliza apenas javascript para isso. E isso nos leva ao arquivo `package.json`.

Se você selecionou como um projeto vanilla, verá que não há nenhuma dependência externa. Apenas o `Babel` que irá transpilar o código para garantir compatibilidade com todos os browsers. Se você selecionou algum framework como React ou Vue, vai ver que eles são a única dependência do seu projeto. E isso é sensacional! 🤩🤩🤩

## Configurando o projeto para que seu componente possa ser utilizado em outro projeto

Como comentado anteriormente é no arquivo `webpack.config.js` que vamos fazer toda a configuração.

### Entendendo as propriedades principais do ModuleFederationPlugin

-  Propriedade name: É o nome do seu módulo. É também como ele será chamado em outros projetos como em `import x from name`
- Propriedade filename: É o nome do arquivo que será gerado no build onde irá conter o "contrato" do seu componente.
- Propriedade shared: É aqui que você pode declarar quais pacotes você tem como dependência e que podem ser compartilhados. Por exemplo, se você tem dois componentes remotos em tela e os dois utilizam React, não é necessário importar o React duas vezes, essa é uma dependência que pode ser compartilhada entre todos os componentes em tela.
- Propriedade exposes: É aqui que declaramos quais partes do nosso projeto queremos expor como um micro front-end. Podemos expor um único arquivo ou vários. E a declaração é algo muito simples. A chave é o nome do módulo e o valor é o caminho do arquivo.

```json
....
plugins: [
        new VueLoaderPlugin(),
        new ModuleFederationPlugin({
            name: "YourPackageName",
            filename: "remoteEntry.js",
            remotes: {},
            exposes: {
                 "yourModuleName": "./src/yourModule/index.js",
            },
            shared: require("./package.json").dependencies,
        }),
```

Apenas com essas configurações quando dermos o build na aplicação será criado o arquivo `remoteEntry.js` que contém todo o manifesto do seu módulo, quais dependências ele tem, quais funções, outros arquivos e etc.

Agora que tudo está confgurado pode dar um build no projeto e exponha a pasta dos artefatos do build (geralemtne é a pasta 'dist') em um servidor http. Caso você não tenha nenhum servidor rodando, você pode inicar um com o pacote _http-server_ disponível no npm (https://www.npmjs.com/package/http-server).

Conforme a documentação do pacote, você pode instalar ele globalmente com
```
npm install --global http-server
```
E para utilizar basta executa-lo na sua pasta de artefatos do build, como por exemplo:
```
 http-server ./dist
```
Pronto! seu arquivo de manifesto do seu modulo remoto está acessível via http. 🥳🎉🎉

### Utilizando seu módulo em outro projeto

Em outro terminal crie um novo projeto utilizando o '_create-mf-app_' igual para o primeiro.

Após seu projeto criado, você pode importar o módulo exportado do primeiro projeto como se fosse um pacote do npm. O seu projeto número dois terá como depência remota o projeto número um.

E para isso você precisa referenciar o projeto número um na propriedade "remotes" da configuração do plugin do webpack, conforme já descrito anteriormente.

a declaração da dependência remota é um pouquinho diferente da declaração de um pacote npm. Veja as diferenças:

_package.json_
```json
{
 //...
 "dependencies": {
    "axios": "^0.27.2",
 }
}
```

_webpack.config.js_
```javascript
  //...
  plugins: [
        new ModuleFederationPlugin({
            // ...
            remotes: {
                 "App1": "http://localhost:8080/remoteEntry.js",
            },
            // ...
        }),
  //...
  ]
```
Para declarar nosso módulo remoto nós damos um nome a ele e referenciamos o seu endereço da internet. Se você está executando o projeto um em localhost com o http-server a porta deve ser a que o http-server está rodando. nesse caso do exemplo é a 8080.


É só isso gente! Basta uma configuração muito simples no plugin para que um projeto referencie o outro e eles possam compartilhar funcionalidades!✨🌟🤩✨🌟🤩

Para mais informações sobre o Module Federation, exemplos de aplicações, documentação e etc. Vou deixar alguns links.
 - https://webpack.js.org/concepts/module-federation/
 - https://github.com/module-federation
 - https://github.com/module-federation/module-federation-examples
 
Também um artigo de um case bem legal da Localiza
 - https://medium.com/localizalabs/module-federation-o-futuro-do-microfrontend-4fed87983ec2