Micro Front-end com Module Federation: Entenda Como Funciona
Parte 2 - Micro Front-end com Module Federation: Construindo Aplicações Extensíveis
Neste artigo
Introdução
O Module Federation, introduzido no Webpack 5, revolucionou a forma como aplicações web podem compartilhar módulos em tempo de execução. Antes, o compartilhamento entre aplicações dependia de builds monolíticos ou bibliotecas externas, mas agora é possível carregar código de outra aplicação dinamicamente, mantendo isolamento e compatibilidade.
No coração do Module Federation estão três conceitos-chave:
- Host Application
- Remote Application
- Shared Dependencies
1. Host Application
O host é a aplicação principal que consome módulos externos. Ele define quem irá consumir, mas não necessariamente quem irá fornecer.
Principais responsabilidades do host:
- Carregar módulos remotos dinamicamente.
- Resolver dependências compartilhadas com os remotes.
- Integrar os módulos carregados à sua própria aplicação, incluindo rotas, UI e estado.
Em tempo de execução, o host faz requisições para os arquivos remoteEntry.js dos remotes. Cada remoteEntry.js é um manifesto gerado pelo Webpack, contendo informações sobre módulos expostos e suas dependências.
Exemplo Conceitual:
const remotes = {
mfeDashboardOne: "http://localhost:4201/remoteEntry.js",
mfeDashboardTwo: "http://localhost:4202/remoteEntry.js"
};
Aqui, o host sabe onde buscar os remotes, mas não precisa conhecer os detalhes internos de implementação de cada módulo.
2. Remote Application
O remote é a aplicação que exponibiliza seus módulos para consumo externo. Ele não precisa saber quem vai usar os módulos; sua responsabilidade é disponibilizá-los de forma clara e consistente.
Principais conceitos:
- Exposes: Define quais módulos podem ser consumidos externamente.
- Entry Points: Arquivos como
remoteEntry.jsque registram os módulos expostos. - Independência: Cada remote pode ter seu próprio ciclo de deploy, dependências e versão.
Exemplo de configuração do remote:
module.exports = withModuleFederationPlugin({
name: "mfe-dashboard-one",
exposes: {
"./Component": "./src/app/app.component.ts",
},
shared: {
...shareAll({ singleton: true, strictVersion: true, requiredVersion: "auto" })
}
});
name: Nome único do remote, usado pelo host para identificação.exposes: Caminho dos módulos que serão consumidos externamente.shared: Bibliotecas que podem ser reutilizadas para evitar duplicação.
3. Shared Dependencies
Um dos maiores desafios de micro front-ends é o gerenciamento de dependências. Sem cuidado, cada micro front-end poderia carregar sua própria cópia de bibliotecas comuns (Angular, React, Lodash), resultando em:
- Bundles maiores.
- Incompatibilidades de versão.
- Bugs complexos e difíceis de reproduzir.
O Module Federation resolve isso através do shared, permitindo que uma única instância de cada biblioteca seja compartilhada entre host e remotes.
Configuração típica de shared:
shared: {
...shareAll({
singleton: true,
strictVersion: true,
requiredVersion: "auto"
})
}
Explicando cada opção:
- singleton: true: Garante que apenas uma instância da biblioteca será carregada. Fundamental para frameworks como Angular.
- strictVersion: true: Exige que todas as aplicações usem a mesma versão da biblioteca.
- requiredVersion: "auto": O Webpack define automaticamente a versão com base no
package.json.
Isso garante compatibilidade e performance, evitando que cada módulo carregue sua própria versão de dependências comuns.
RemoteEntry.js e Runtime do Module Federation
O arquivo remoteEntry.js é o coração do módulo federado. Ele contém:
- Informações sobre os módulos expostos (
exposes). - Mapas de dependências compartilhadas (
shared). - Código para registrar os módulos no runtime do Webpack.
Quando o host carrega um módulo remoto, o Webpack cria dinamicamente promises para cada dependência e injeta os módulos carregados no contexto do host.
Fluxo de carregamento:
- Host solicita
remoteEntry.js. - Webpack registra os módulos expostos e suas dependências.
- Host consome os módulos usando
loadRemoteModule(). - Webpack resolve as dependências compartilhadas, evitando duplicação.
- Componente ou módulo é renderizado no host.
Estratégias de Versionamento
Gerenciar versões em micro front-ends é crucial. Algumas estratégias:
- Single Version: Todos os micro front-ends usam a mesma versão das bibliotecas compartilhadas. Fácil de manter, mas menos flexível.
- Multiple Versions: Permite múltiplas versões da mesma biblioteca, isolando cada remote. Útil quando migrações graduais são necessárias, mas aumenta o bundle.
- Semver Awareness: Usar
requiredVersion: "auto"estrictVersionpara garantir compatibilidade mínima, sem forçar versões idênticas.
Boas práticas:
- Sempre documentar a versão das dependências compartilhadas.
- Evitar alterações bruscas de versão sem testar a compatibilidade com o host.
- Para grandes sistemas, considerar pipelines de CI/CD que validem todos os remotes juntos antes do deploy.
Comunicação entre Host e Remotes
Além de compartilhar módulos, muitas vezes precisamos compartilhar estado ou dados entre host e remotes. Algumas abordagens comuns:
- Eventos Customizados: Remotes emitem eventos, host escuta e reage.
- Serviços Compartilhados: Criar serviços singleton que podem ser injetados em múltiplos módulos.
- State Management Global: Usar bibliotecas como NgRx, Redux ou RxJS BehaviorSubjects compartilhados via
shared.
Exemplo simplificado usando BehaviorSubject compartilhado:
// shared-state.ts
import { BehaviorSubject } from 'rxjs';
export const userState = new BehaviorSubject<{name: string}>({ name: '' });
Tanto host quanto remotes podem importar userState e reagir a mudanças em tempo real.
Desafios Técnicos do Module Federation
Apesar de poderoso, Module Federation exige cuidado em:
- Carregamento assíncrono: Dependências críticas podem atrasar a renderização.
- Bundle duplication: Evitar múltiplas versões de bibliotecas.
- Debugging: Problemas de versão e dependências podem ser difíceis de rastrear.
- Rotas complexas: Micro front-ends com múltiplas rotas internas requerem configuração adequada no host.
Conclusão
Nesta parte, entendemos profundamente como o Module Federation funciona:
- Host consome, remote expõe.
- Shared dependencies resolvem conflitos de bibliotecas.
remoteEntry.jsatua como ponto de registro de módulos.- Estratégias de versionamento e compartilhamento de estado são cruciais para sucesso em sistemas grandes.
Na Parte 3, vamos aplicar tudo isso em Angular, construindo um host e dois remotes, detalhando código, rotas, webpack e comunicação entre módulos, mostrando o passo a passo completo.