Voltar para o Blog

Micro Frontends: Como Escalar Aplicacoes Frontend Com Arquitetura Modular

Olá HaWkers, se você trabalha em uma empresa com times grandes de frontend ou em um monolito que está difícil de manter, provavelmente já ouviu falar de micro frontends. Essa arquitetura promete trazer para o frontend os mesmos benefícios que microserviços trouxeram para o backend.

Mas será que vale a pena? E como implementar de forma que não vire um pesadelo? Vamos explorar isso em profundidade.

O Que São Micro Frontends

Micro frontends é uma arquitetura onde uma aplicação web é dividida em partes menores e independentes, cada uma desenvolvida, testada e implantada separadamente.

Analogia simples:

  • Monolito frontend = Uma loja onde todos os departamentos compartilham o mesmo caixa
  • Micro frontends = Um shopping onde cada loja opera independentemente

Características principais:

  1. Independência de deploy - Cada parte pode ser atualizada sem afetar as outras
  2. Autonomia de time - Times diferentes podem trabalhar em partes diferentes
  3. Tecnologia agnóstica - Cada parte pode usar tecnologias diferentes
  4. Isolamento de falhas - Problemas em uma parte não derrubam o sistema inteiro

🏗️ Contexto: O termo foi cunhado em 2016, mas ganhou tração a partir de 2019 quando empresas como IKEA, Spotify e DAZN compartilharam suas experiências.

Quando Usar Micro Frontends

Antes de adotar essa arquitetura, você precisa ter certeza que ela resolve um problema real:

Sinais de Que Você Precisa

Indicadores organizacionais:

  • Múltiplos times trabalhando no mesmo frontend
  • Conflitos constantes de merge
  • Deploy bloqueado esperando outras features
  • Tempo de build/teste muito longo
  • Dificuldade de onboarding de novos devs

Indicadores técnicos:

  • Aplicação com centenas de milhares de linhas de código
  • Bundle size muito grande
  • Performance de desenvolvimento degradada
  • Testes lentos demais

Sinais de Que Você NÃO Precisa

Quando evitar:

  • Time pequeno (menos de 10 desenvolvedores frontend)
  • Aplicação de tamanho médio
  • Domínio de negócio fortemente acoplado
  • Startup em fase inicial
  • Time sem experiência em arquitetura distribuída

Regra de ouro: Se você não tem problemas claros de escala ou organização, micro frontends vai adicionar complexidade sem benefício.

Padrões de Implementação

Existem várias formas de implementar micro frontends. Vamos ver as principais:

1. Module Federation (Webpack 5+)

A abordagem mais moderna e popular atualmente:

// webpack.config.js do Host (Shell)
const { ModuleFederationPlugin } = require('webpack').container;

module.exports = {
  plugins: [
    new ModuleFederationPlugin({
      name: 'host',
      remotes: {
        // Carrega micro frontend de carrinho de outro servidor
        cartMfe: 'cartMfe@http://localhost:3001/remoteEntry.js',
        // Carrega micro frontend de produtos
        productsMfe: 'productsMfe@http://localhost:3002/remoteEntry.js',
      },
      shared: {
        react: { singleton: true, requiredVersion: '^18.0.0' },
        'react-dom': { singleton: true, requiredVersion: '^18.0.0' },
      },
    }),
  ],
};

// webpack.config.js do Micro Frontend de Carrinho
module.exports = {
  plugins: [
    new ModuleFederationPlugin({
      name: 'cartMfe',
      filename: 'remoteEntry.js',
      exposes: {
        './Cart': './src/components/Cart',
        './CartButton': './src/components/CartButton',
      },
      shared: {
        react: { singleton: true, requiredVersion: '^18.0.0' },
        'react-dom': { singleton: true, requiredVersion: '^18.0.0' },
      },
    }),
  ],
};

Uso no Host:

// App.jsx no Host
import React, { Suspense } from 'react';

// Importação dinâmica do micro frontend remoto
const RemoteCart = React.lazy(() => import('cartMfe/Cart'));
const RemoteProducts = React.lazy(() => import('productsMfe/ProductList'));

function App() {
  return (
    <div className="app">
      <header>Minha Loja</header>

      <main>
        <Suspense fallback={<div>Carregando produtos...</div>}>
          <RemoteProducts />
        </Suspense>
      </main>

      <aside>
        <Suspense fallback={<div>Carregando carrinho...</div>}>
          <RemoteCart />
        </Suspense>
      </aside>
    </div>
  );
}

2. Single-SPA

Framework dedicado para orquestrar micro frontends:

// root-config.js
import { registerApplication, start } from 'single-spa';

// Registra micro frontend de navegação
registerApplication({
  name: '@myorg/navbar',
  app: () => System.import('@myorg/navbar'),
  activeWhen: ['/'],
});

// Registra micro frontend de produtos (ativo em /products)
registerApplication({
  name: '@myorg/products',
  app: () => System.import('@myorg/products'),
  activeWhen: ['/products'],
});

// Registra micro frontend de checkout (ativo em /checkout)
registerApplication({
  name: '@myorg/checkout',
  app: () => System.import('@myorg/checkout'),
  activeWhen: ['/checkout'],
});

start();

3. iFrames (Abordagem Simples)

Para isolamento total quando necessário:

// Componente wrapper para micro frontend via iframe
function MicroFrontendFrame({ src, title, height = '600px' }) {
  return (
    <iframe
      src={src}
      title={title}
      style={{
        width: '100%',
        height,
        border: 'none',
      }}
      sandbox="allow-scripts allow-same-origin allow-forms"
    />
  );
}

// Uso
<MicroFrontendFrame
  src="https://checkout.myapp.com"
  title="Checkout"
  height="800px"
/>;

Comunicação Entre Micro Frontends

Um dos maiores desafios é fazer os micro frontends se comunicarem:

Event Bus

Padrão simples e eficaz:

// eventBus.js - Compartilhado entre todos os MFEs
class EventBus {
  constructor() {
    this.events = {};
  }

  subscribe(event, callback) {
    if (!this.events[event]) {
      this.events[event] = [];
    }
    this.events[event].push(callback);

    // Retorna função para unsubscribe
    return () => {
      this.events[event] = this.events[event].filter((cb) => cb !== callback);
    };
  }

  publish(event, data) {
    if (this.events[event]) {
      this.events[event].forEach((callback) => callback(data));
    }
  }
}

// Singleton global
window.eventBus = window.eventBus || new EventBus();
export default window.eventBus;

Uso:

// No MFE de Produtos
import eventBus from '@shared/eventBus';

function ProductCard({ product }) {
  const addToCart = () => {
    eventBus.publish('cart:add', {
      productId: product.id,
      quantity: 1,
    });
  };

  return (
    <div className="product">
      <h3>{product.name}</h3>
      <button onClick={addToCart}>Adicionar ao Carrinho</button>
    </div>
  );
}

// No MFE de Carrinho
import eventBus from '@shared/eventBus';

function Cart() {
  const [items, setItems] = useState([]);

  useEffect(() => {
    const unsubscribe = eventBus.subscribe('cart:add', (data) => {
      setItems((prev) => [...prev, data]);
    });

    return unsubscribe;
  }, []);

  return <div>{/* Renderiza itens */}</div>;
}

Custom Events (Browser Native)

Alternativa sem dependências:

// Publicar
window.dispatchEvent(
  new CustomEvent('cart:updated', {
    detail: { items: cartItems },
  })
);

// Subscrever
window.addEventListener('cart:updated', (event) => {
  console.log('Carrinho atualizado:', event.detail.items);
});

Desafios e Armadilhas

Micro frontends trazem complexidade. Esteja preparado:

1. Consistência de UI

Problema: Cada MFE pode ter estilos diferentes.

Solução: Design System compartilhado

// @myorg/design-system - Pacote npm interno
export { Button } from './components/Button';
export { Card } from './components/Card';
export { theme } from './theme';
export { GlobalStyles } from './GlobalStyles';

2. Duplicação de Dependências

Problema: React carregado múltiplas vezes.

Solução: Shared dependencies no Module Federation

shared: {
  react: {
    singleton: true,
    requiredVersion: '^18.0.0',
    eager: true
  }
}

3. Performance

Problema: Múltiplas requisições de rede.

Soluções:

  • Prefetch de MFEs críticos
  • Service workers para cache
  • CDN compartilhado

4. Testes End-to-End

Problema: Como testar a aplicação completa?

Solução: Ambiente de integração dedicado

# docker-compose.integration.yml
services:
  shell:
    build: ./packages/shell
    ports:
      - '3000:3000'

  cart-mfe:
    build: ./packages/cart
    ports:
      - '3001:3001'

  products-mfe:
    build: ./packages/products
    ports:
      - '3002:3002'

Ferramentas do Ecossistema

O ecossistema evoluiu bastante em 2024-2025:

Build e Bundling

Ferramenta Uso Suporte MFE
Webpack 5 Module Federation nativo Excelente
Vite vite-plugin-federation Bom
Rspack Module Federation Excelente
esbuild Customização necessária Básico

Frameworks de Orquestração

Framework Complexidade Maturidade
Single-SPA Alta Alta
Module Federation Média Alta
Qiankun Média Média
Luigi (SAP) Alta Alta

Monorepo Tools

Para gerenciar múltiplos MFEs:

  • Nx - Mais popular, ótimo suporte a MFE
  • Turborepo - Performance, integração Vercel
  • Lerna - Clássico, menos features
  • pnpm workspaces - Leve e eficiente

Conclusão

Micro frontends são uma ferramenta poderosa para escalar times e aplicações grandes. Mas como toda arquitetura distribuída, trazem complexidade que precisa ser justificada.

Use se:

  • Tem múltiplos times independentes
  • Aplicação genuinamente grande
  • Problemas claros de escala
  • Time experiente em sistemas distribuídos

Evite se:

  • Time pequeno ou médio
  • Aplicação de tamanho normal
  • Buscando solução para problemas organizacionais
  • Sem experiência em arquitetura distribuída

Se você decidir implementar, comece pequeno: extraia um MFE, aprenda com os desafios, e escale gradualmente. A pior coisa que você pode fazer é migrar tudo de uma vez.

Para complementar seu conhecimento em arquiteturas modernas de frontend, recomendo conferir o artigo Vite vs Webpack em 2025 onde você vai entender as ferramentas de build que suportam essas arquiteturas.

Bora pra cima! 🦅

💻 Domine JavaScript de Verdade

O conhecimento que você adquiriu neste artigo é só o começo. Há técnicas, padrões e práticas que transformam desenvolvedores iniciantes em profissionais requisitados.

Invista no Seu Futuro

Preparei um material completo para você dominar JavaScript:

Formas de pagamento:

  • 1x de R$9,90 sem juros
  • ou R$9,90 à vista

📖 Ver Conteúdo Completo

Comentários (0)

Esse artigo ainda não possui comentários 😢. Seja o primeiro! 🚀🦅

Adicionar comentário