Cinco squads no mesmo repositório frontend produzem conflito de merge diário, build que cresce a cada sprint e release travado por time que não toca na sua feature. Micro-frontends separam a aplicação em unidades que cada squad publica sozinho. A troca aparece depois da adoção: runtime, deploy e debug ganham peças novas.

O problema que a arquitetura ataca

Micro-frontends se pagam com três condições simultâneas: 3+ times no mesmo frontend, release independente por squad e sobreposição de código pequena entre eles. Cam Jackson, no artigo Micro Frontends (martinfowler.com, 2019), descreve o objetivo como aumentar a capacidade de times que trabalham no mesmo produto, e o custo aparece justamente na manutenção da coerência entre peças publicadas em velocidades diferentes.

Um squad de dois times com fronteira de domínio clara paga a infraestrutura de micro-frontends e não recebe velocidade de volta. Meça o tempo entre abrir o PR e publicar em produção antes de assinar qualquer migração.

Três formas de compor a página

Integração em build: Module Federation

O Module Federation do Webpack carrega módulos de outra aplicação em runtime. Cada micro-frontend é uma aplicação standalone que exporta um remote entry, e o shell decide qual entra em cada rota. Rspack e o projeto Module Federation independente mantêm o mesmo conceito.

// webpack.config.js: shell
new ModuleFederationPlugin({
  name: 'shell',
  remotes: {
    checkout: 'checkout@https://checkout.example.com/remoteEntry.js',
    catalog: 'catalog@https://catalog.example.com/remoteEntry.js',
  },
})

Integração em runtime com iframe

iframe isola estilos, JavaScript e ciclo de vida por completo. A comunicação vira postMessage, e o preço aparece em acessibilidade, SEO e compartilhamento de sessão entre as partes.

Composição no servidor: SSI e ESI

O servidor monta a página antes de enviar e o cliente recebe HTML pronto. A orquestração sai do browser, e a exigência passa a ser infraestrutura de include mais um contrato de HTML claro entre times.

Estrutura de repositórios na prática

shell-app/              # Monta a página e o layout base
├── src/
│   ├── Header.tsx      # Compartilhado entre todos
│   ├── Footer.tsx      # Compartilhado entre todos
│   └── MicroApp.tsx    # Loader dos micro-frontends
│
checkout/               # Squad de checkout
├── src/
│   ├── Checkout.tsx
│   ├── Payment.tsx
│   └── Shipping.tsx
│
catalog/                # Squad de catálogo
├── src/
│   ├── ProductList.tsx
│   └── ProductDetail.tsx

Custos que aparecem na operação

  • Comunicação: dado compartilhado exige event bus, store no shell ou contrato de props. Sem isso, cada squad guarda a própria cópia do carrinho.
  • Estilo: design system em pacote versionado é pré-requisito. Tokens e componentes base saem de um lugar só.
  • Performance: cada bundle extra custa requests HTTP e parse de JS. O shell cuida de preload e de lazy load por rota.
  • Debug: stack trace que atravessa dois bundles pede dois times no mesmo call.
  • Deploy: dependência entre squads devolve o deploy coordenado que a arquitetura prometia eliminar.

Times que chegam ao quinto custo descobrem que a independência de deploy sobrevive só enquanto os contratos entre as peças permanecem estáveis.

Quando adotar

Adote quando três ou mais squads publicam no mesmo produto sem coordenação de release, o gargalo de merge já aparece em métrica e cada time consegue explicar a fronteira de domínio da própria fatia. A velocidade de time ganha espaço; o custo de infraestrutura entra no orçamento do mesmo jeito.

Quando ficar no monólito

Dois squads ou menos, features que se sobrepõem e release compartilhado pedem monólito modular com pastas e módulos por domínio. Arquitetura nova não conserta processo de time quebrado, e o monólito bem dividido publica com a mesma rapidez.

Referências