Micro Frontends aplicam o conceito de microservices no frontend: cada time desenvolve, deploya e mantém uma parte do app independentemente.
O Problema dos Monolitos Frontend
App grande:
- 500k+ linhas de código
- 20+ devs
- Deploy demora 30min
- Um bug quebra todo o app
Micro Frontends: Independência
Divida o app por domínios de negócio:
E-commerce:
- Header (Team A)
- Product Catalog (Team B)
- Cart (Team C)
- Checkout (Team D)
Cada time:
- ✅ Usa framework próprio (React, Vue, Svelte)
- ✅ Deploya independentemente
- ✅ Tem pipeline CI/CD isolado
Module Federation (Webpack 5)
A solução mais popular. Permite compartilhar código em runtime:
Host App
// webpack.config.js (Host)
const { ModuleFederationPlugin } = require('webpack').container;
module.exports = {
plugins: [
new ModuleFederationPlugin({
name: 'host',
remotes: {
cart: 'cart@https://cart.example.com/remoteEntry.js',
catalog: 'catalog@https://catalog.example.com/remoteEntry.js',
},
shared: {
react: { singleton: true },
'react-dom': { singleton: true },
},
}),
],
};
Uso
// App.jsx (Host)
import React, { lazy, Suspense } from 'react';
const Cart = lazy(() => import('cart/Cart'));
const Catalog = lazy(() => import('catalog/ProductList'));
export default function App() {
return (
<div>
<h1>E-commerce</h1>
<Suspense fallback={<div>Loading catalog...</div>}>
<Catalog />
</Suspense>
<Suspense fallback={<div>Loading cart...</div>}>
<Cart />
</Suspense>
</div>
);
}
Remote App (Cart)
// webpack.config.js (Cart)
new ModuleFederationPlugin({
name: 'cart',
filename: 'remoteEntry.js',
exposes: {
'./Cart': './src/Cart',
},
shared: {
react: { singleton: true },
'react-dom': { singleton: true },
},
});
O host carrega o cart em runtime, sem rebuild.
Single-SPA: Framework Agnostic
Permite combinar React, Vue, Angular no mesmo app:
import { registerApplication, start } from 'single-spa';
registerApplication({
name: '@company/header',
app: () => import('@company/header'), // React
activeWhen: ['/'],
});
registerApplication({
name: '@company/cart',
app: () => import('@company/cart'), // Vue
activeWhen: ['/cart'],
});
registerApplication({
name: '@company/checkout',
app: () => import('@company/checkout'), // Angular
activeWhen: ['/checkout'],
});
start();
Cada app monta/desmonta baseado na rota.
Comunicação Entre Microfrontends
Opção 1: Event Bus
// event-bus.js
const eventBus = new EventTarget();
export const publish = (event, data) => {
eventBus.dispatchEvent(new CustomEvent(event, { detail: data }));
};
export const subscribe = (event, callback) => {
eventBus.addEventListener(event, (e) => callback(e.detail));
};
// Cart.jsx
import { publish } from './event-bus';
function addToCart(product) {
publish('cart:item-added', product);
}
// Header.jsx
import { subscribe } from './event-bus';
subscribe('cart:item-added', (product) => {
updateCartCount();
});
Opção 2: Shared State (Context API)
// shared-context.js
import { createContext } from 'react';
export const CartContext = createContext();
// Host
<CartContext.Provider value={{ items, addItem }}>
<Cart />
<Checkout />
</CartContext.Provider>
Opção 3: URL/Query Params
/products?cartCount=3
Simples, mas limitado.
Quando Usar Micro Frontends?
✅ Múltiplos times (5+ teams)
✅ App gigante (500k+ LOC)
✅ Times querem frameworks diferentes
✅ Deploy independente é crítico
❌ Time pequeno (menos de 10 devs)
❌ App simples
❌ Uniformidade é importante
Trade-offs
Vantagens
✅ Autonomia de times
✅ Deploys independentes
✅ Tech stack flexível
✅ Escala organizacionalmente
Desvantagens
❌ Complexidade operacional (CI/CD, monitoring)
❌ Bundle duplicado (React carregado múltiplas vezes?)
❌ Consistência de UI difícil
❌ Performance overhead (network requests)
Performance: Otimizações
Estratégia 1: Shared Dependencies
shared: {
react: { singleton: true, eager: true }, // Carrega só 1x
}
Estratégia 2: Preload
<link rel="preload" href="https://cart.example.com/remoteEntry.js" as="script">
Estratégia 3: CDN + Cache
Hospede remotes em CDN com cache longo:
https://cdn.example.com/cart/v1.2.3/remoteEntry.js
Alternativas Modernas
Turbo + Monorepo
Não é micro frontend, mas resolve múltiplos times:
apps/
web/ (Next.js)
admin/ (Next.js)
packages/
ui/ (Shared components)
database/ (Prisma)
{
"scripts": {
"build": "turbo run build",
"dev": "turbo run dev --parallel"
}
}
Times trabalham independentemente, mas código é unificado.
Conclusão: Micro Frontends resolvem problemas organizacionais, não técnicos. Se seu time é pequeno, use monorepo. Se tem mais de 10 times, considere micro frontends.