DEV Community

Cover image for Module Federation e as principais propriedades do Share
Helton
Helton

Posted on • Edited on

Module Federation e as principais propriedades do Share

O Module Federation é um plugin do webpack que permite trabalhar com microfrontends removendo uma carga de complexidade e com algum ganho de performance que iremos descrever nesse breve artigo.

Disclaimer:

  • Esse artigo não é um tutorial! é apenas um artigo de referência para descrever as opções da propriedade Share do plugin
  • Teremos o termo shell mais usado pela comunidade como a definição de um container que abriga ali vários microfrontends, e microfrontend chamaremos de MF ou MFs(plural) até o final do artigo.

Existem hoje algumas possibilidades de se trabalhar com MFs, algumas delas usando outros frameworks como single-spa, estratégias com monorepo como NX ou Angular Worskpace e até mesmo alguma proposta de compiler que tenta resolver o problema de duplicação de código que as abordagens citadas anteriormente não se propõem a fazer “meados de maio de 2022".

Mas o que seria a duplicação de código?

Tenha o seguinte cenário em mente, temos uma aplicação chamada de shell com a versão 17 do react e um MF utilizando a mesma versão, quando geramos o bundle final que vai para a produção temos a duplicação do core da biblioteca, pois mesmo que essas aplicações estejam utilizando a mesma versão da biblioteca ela não é compartilhada entre as mesmas, e esse cenário pode ser o mesmo utilizando frameworks como angular, então imagina uma aplicação com vários MFs, considere o tamanho do bundle que será consumido pelo client/browser, e existem algumas abordagens para resolver esse problema no processo de build, mas requer alguma complexidade.

E o que muda com o Module Federation

É exatamente nesse ponto que o module federation entra, ele não vem apenas para ser mais uma solução de MFs e sim para resolver o problema de duplicação de código.

Voltando para o mesmo cenário citado, as duas aplicações com a mesma dependência do React na versão 17, que agora utilizando module federation conseguirmos através da propriedade share criar estratégias que forneçam a possibilidade de ter o core da biblioteca compartilhado entre o Shell e os MFs gerando um ganho de performance considerável já que o cliente/browser vai consumir apenas o bundle necessário para a execução da aplicação.

Agora vamos entender as Opções do plugin

name: string é o nome que definimos para o nosso MF, e é uma boa prática que ela seja a mesma que o nome da aplicação.

filename: string é o nome dado ao entrypoint que o webpack vai gerar para que possa ser consumido pela aplicação shell ex: “meuModuleRemoteEntry.js".

library: object é um objeto com opções que ajudam descrever como um código/chunck gerado que foi exposto será armazenado e recuperado, essa propriedade tem seu próprio leque de configurações, como a proposta desse artigo é falar um pouco mais sobre a propriedade share vou deixar o link para documentação.

Mas no geral você pode configurar como consumir esse MF que vai expor, como uma variável ou módulo etc...

share: object -> É onde declaramos todas as bibliotecas que podem ser compartilhadas entre os MFs, quando o shell compartilha uma determinada lib com os MFs e os MFs tem a mesma lib compatível com o shell os MFs consomem a lib compartilhada pelo shell do contrário acabam utilizando a sua própria versão lib na aplicação, sendo assim teremos duas libs iguais mais que podem estar versões diferentes.

Entendo isso temos 3 formas de declarar as bibliotecas compartilhadas.

  1. Array:
shared: ['react']
Enter fullscreen mode Exit fullscreen mode
  1. objeto
shared: {
        // Será utilizado a maior versão do React
        // que pode se >= 17.0 and < 18
          react: '^17.0.2',
        },
Enter fullscreen mode Exit fullscreen mode
  1. Objeto da biblioteca com algumas opções de compartilhamento:
shared: {
         // adiciona o react como módulo compartilhado
         // e configura mais algumas opções
         react: {
           requiredVersion: '^17.0.2',
           singleton: true,
          },
        },
Enter fullscreen mode Exit fullscreen mode

E essas opções de compartilhamento podem ser:

eager: boolean -> essa opção vai permitir fornecer um um módulo inicial no chunck da aplicação sendo ele o módulo principal ou um fallback, o módulo vai ser compilado junto com seu MF em vez de ser solicitado de forma assíncrona.

import: false | string -> fornece o módulo que deve ser adicionado ao Shared Scope, se o módulo compartilhado no Shared Scope não for encontrado ele vai usar o módulo fornecido no import como fallback

shareKey: string -> O módulo compartilhado é pesquisado através dessa chave do escopo compartilhado.

shareScope: string -> O nome do escopo compartilhado.
Exemplo da utilização do import, shareKey e shareScope

shared: {
        'my-extension-lib': {
          import: 'lib', // O pacote "lib" será usado como pacote fornecido e um substituto
          shareKey: 'shared-lib', // Nome dado ao módulo que será colocado no Share Scope
          shareScope: 'default', // Nome dado ao Share Scope
          version: '1.2.3', // A versão da lib
          requiredVersion: '^1.0.0', // A versão minima solicitada pela lib
        },
      },
Enter fullscreen mode Exit fullscreen mode

packageName: string -> Necessário APENAS para quando precisamos determinar uma versão de biblioteca que não pode ser determinada automaticamente.

requiredVersion: string -> Usado para declarar a versão especifica de uma biblioteca.

singleton: boolean ->Essa opção permite ter uma única versão do módulo que foi compartilhado no escopo de compartilhamento o que seria o shell.

Usar mais do que uma versão de biblioteca não é uma escolha muito interessante, pois quando falamos de bibliotecas que mantém o estado, você utilizar mais de uma versão fornece automaticamente a duplicidade de um Estado Principal para a aplicação.

Por esse motivo o Module Federation permite definir bibliotecas como singletons, se temos versões da biblioteca compatíveis considerando a Minor version o Module Federation vai decidir pela versão mais recente pela lib.

É possível usar versões diferentes de uma lib usando um range no requiredVersion, mesmo optando pelo singleton: true e strictVersion true.

strictVersion: boolean -> Essa opção deve rejeitar o módulo compartilhado e emitir um erro se a versão não é válida, por default ela é true quando um módulo de fallback está disponível e não estamos utilizando a opção singleton, a outra opção é false mas ele não tem efeito se houver declarado a versão obrigatória da biblioteca.

version: false | string -> A versão do módulo fornecido, permite que o webpack substitua uma versão correspondente Minor version, mas não a Major version.

Em resumo falando sobre Share e o compartilhamento de libs, o Shell sempre vai assumir a responsabilidade de manter a versão mais atual da biblioteca compartilhada com os MFs, mas quando o carregamento dos MFs não for feito de forma estática em suma quando for feito de forma dinâmica, e o Shell tenha uma versão menor do que o MF cada um vai ficar responsável por subir sua própria versão da biblioteca.

Ainda podemos através do compartilhamento dos MFs de forma dinâmica carregar dinamicamente apenas o ponto de entrada remoto no início do programa e carregar o micro frontend posteriormente sob demanda. Ao dividir isso em dois processos de carregamento, o comportamento é exatamente o mesmo do compartilhamento de forma estática ("clássica"). A razão é que neste caso os metadados da entrada remota estão disponíveis com antecedência suficiente para serem considerados durante a negociação das versões.

Caso eles sejam estáticos essas libs vão ser conhecidas no tempo de build tanto do shell quanto do microfrontend e com isso será carregado a versão mais atual da lib em questão.

Bom é isso, Module Federation trás um cenário bem interessante e com muitas possibilidades para se trabalhar com Microfrontends.

Referências:
Documentação Webpack
Angular Architects

Top comments (0)