Como criar micro-frontends modulares com Vite e Module Federation
Aprenda a desacoplar aplicações web modernas usando Module Federation e Vite, garantindo compartilhamento eficiente de dependências entre times.
Resumo
- A arquitetura de micro-frontends divide aplicações monolíticas em partes menores gerenciadas por equipes independentes.
- O Module Federation permite carregar código em tempo de execução diretamente de outras aplicações na web.
- O Vite acelera o ambiente de desenvolvimento usando compilação sob demanda com ES modules nativos.
- O compartilhamento de dependências evita duplicação de bibliotecas pesadas como o React no navegador.
- O versionamento rigoroso e a definição clara de contratos evitam quebras inesperadas em produção.
Por que dividir o seu painel web em partes menores
Imagine que sua empresa cresceu, o time de engenharia dobrou de tamanho e todo mundo tenta alterar o mesmo código-fonte do sistema principal ao mesmo tempo. Na prática, isso significa conflitos constantes no Git, builds lentos que demoram vinte minutos e deploys arriscados onde um erro no módulo de pagamentos derruba o painel inteiro de clientes. Dividir a interface em pedaços menores e independentes resolve esse problema de escala humana e técnica.
Essa abordagem descentralizada, conhecida como arquitetura de micro-frontends, permite que diferentes equipes desenvolvam, testem e publiquem partes isoladas do site ou sistema sem depender de um único repositório gigante. É como um condomínio fechado onde cada casa tem sua própria rotina, mas todas compartilham a mesma rua principal. Se uma equipe quiser atualizar o painel financeiro usando a versão mais recente de uma biblioteca de gráficos, ela faz isso sem travar o trabalho do time de cadastro de usuários.
No entanto, separar tudo em projetos distintos traz um desafio clássico: como fazer essas peças conversarem na tela do usuário sem duplicar código pesado ou deixar o carregamento da página insuportavelmente lento? É aqui que entra a combinação de ferramentas modernas de empacotamento, tema central que também abordamos em estratégias avançadas de como renderizar listas longas em React mantendo 60 FPS para garantir fluidez visual.
Como funciona o Module Federation na prática
Durante muito tempo, integrar códigos de repositórios diferentes no navegador significava usar iframes desajeitados ou gerar pacotes estáticos que precisavam ser reinstalados a cada pequena alteração. O Module Federation muda essa regra do jogo ao permitir que uma aplicação funcione tanto como anfitriã (o container principal) quanto como fornecedora (remota) que entrega pedaços de código sob demanda.
Em termos simples, pense nisso como um sistema de entregas onde o aplicativo principal não guarda todos os móveis no estoque; ele simplesmente pede por encomenda o sofá ou a mesa quando o cliente abre a sala correspondente. Na prática, o navegador baixa o código do módulo remoto apenas quando o usuário navega até aquela seção específica, economizando banda de internet e acelerando a abertura inicial da página.
Essa troca dinâmica acontece graças aos módulos nativos do ecossistema JavaScript moderno, que sabem negociar dependências em tempo de execução. Assim, se o módulo principal e o módulo remoto usam exatamente a mesma versão do React, o navegador baixa a biblioteca apenas uma vez, evitando o desperdício de memória RAM no computador de quem está acessando o sistema.
Configurando o Vite para compartilhar código entre projetos
O Vite se tornou o queridinho dos desenvolvedores por sua velocidade absurda no ambiente de desenvolvimento, baseando-se em ES modules nativos do navegador em vez de empacotar tudo antes de iniciar. Para unir essa velocidade ao Module Federation, utilizamos plugins específicos como o @originjs/vite-plugin-federation, que traduz os conceitos de compartilhamento para o ecossistema rápido do Vite.
Para colocar a mão na massa, o primeiro passo consiste em configurar a aplicação que vai fornecer o código (o microsserviço remoto) expondo suas funcionalidades através do arquivo de configuração do empacotador. Veja abaixo como estruturar o arquivo vite.config.js no projeto remoto:
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import federation from '@originjs/vite-plugin-federation';
export default defineConfig({
plugins: [
react(),
federation({
name: 'remoteApp',
filename: 'remoteEntry.js',
exposes: {
'./Widget': './src/components/Widget.jsx',
},
shared: ['react', 'react-dom'],
}),
],
build: {
target: 'esnext',
minify: false,
cssCodeSplit: false,
},
});Esse bloco de código avisa ao Vite que o componente localizado em src/components/Widget.jsx deve ser empacotado de forma isolada e disponibilizado sob o nome ./Widget para qualquer aplicação externa que pedir por ele. A propriedade shared garante que o React e o React-dom sejam tratados como dependências singleton, ou seja, carregadas uma única vez na memória global da página.
Consumindo o módulo remoto na aplicação principal
Com o componente remoto publicado e acessível via rede, o próximo passo é configurar o aplicativo anfitrião para consumir esse pedaço de código como se ele estivesse na mesma pasta local. Na prática, isso transforma componentes externos em peças nativas da sua árvore de elementos visuais.
Abaixo está um exemplo de configuração no projeto principal (host) para importar o componente remoto exposto no passo anterior:
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import federation from '@originjs/vite-plugin-federation';
export default defineConfig({
plugins: [
react(),
federation({
name: 'hostApp',
remotes: {
remoteApp: 'http://localhost:5001/assets/remoteEntry.js',
},
shared: ['react', 'react-dom'],
}),
],
build: {
target: 'esnext',
minify: false,
},
});Com essa ponte estabelecida no arquivo de configuração, basta importar o componente dentro do código JavaScript ou JSX da aplicação principal utilizando uma chamada assíncrona ou limites de erro adequados para lidar com eventuais quedas de rede:
import React, { lazy, Suspense } from 'react';
const RemoteWidget = lazy(() => import('remoteApp/Widget'));
export default function App() {
return (
<div>
<h1>Painel Principal<h1>
<Suspense fallback={<p>Carregando widget...<p>}>
<RemoteWidget />
<Suspense>
<div>
);
}
O uso do componente lazy combinando com Suspense é fundamental porque o navegador precisa de alguns milissegundos para baixar o arquivo remoteEntry.js pela rede. Enquanto o download acontece, a interface exibe uma mensagem de carregamento amigável em vez de travar a tela.
Gerenciando dependências e evitando conflitos de versões
Compartilhar dependências entre diferentes equipes parece simples no papel, mas exige disciplina rigorosa para evitar o pesadelo conhecido como conflito de versões. Na prática, se o time A atualiza o React para a versão mais recente e o time B continua usando uma versão antiga, o navegador pode se recusar a executar o código ou causar comportamentos bizarros na interface.
Para mitigar esse risco, estabeleça políticas claras de versionamento semântico e utilize o bloqueio rigoroso de versões no arquivo package.json de todos os micro-frontends. Além disso, quando o ecossistema exige bases de dados ou contratos complexos nos bastidores, vale a pena aplicar padrões consistentes, paralelos aos cuidados que discutimos em como desacoplar bases de dados em microsserviços legados.
Outra boa prática operacional consiste em monitorar o desempenho e a latência de carregamento dos arquivos remotos utilizando ferramentas de observabilidade e métricas de infraestrutura, garantindo que a queda de um servidor periférico não inutilize todo o sistema principal. Para aprofundar esses conceitos na prática em produção, confira também nossa análise técnica sobre Banco de dados lento? Como acelerar consultas com índices parciais.
Considerações finais sobre arquiteturas descentralizadas
Adotar micro-frontends com Module Federation e Vite não é uma bala de prata que resolve problemas de organização cultural da noite para o dia; é uma ferramenta poderosa de engenharia para empresas que já enfrentam gargalos reais de escala em monólitos front-end. A autonomia operacional ganha fôlego, permitindo que diferentes times entreguem valor de forma independente e contínua.
Em suma, o sucesso dessa arquitetura depende tanto da escolha correta das ferramentas quanto do rigor nos contratos de integração e na governança das dependências compartilhadas. Planeje bem a divisão dos domínios de negócio antes de escrever a primeira linha de código, mantenha a comunicação entre os times ativa e aproveite a agilidade que a modularidade moderna proporciona ao seu produto web.