Server-Sent Events (SSE): reatividade simples e eficiente no frontend
Como usar Server-Sent Events em microfrontends para fluxos de dados unidirecionais, notificações em tempo real, feature flags dinâmicas e comunicação entre Shell e MFEs.
O Server-Sent Events (SSE) continua sendo uma opção sólida para fluxos de dados unidirecionais no frontend, principalmente em arquiteturas de Micro Frontends (MFEs), onde simplicidade, performance e escalabilidade pesam.
Hoje, o SSE vem sendo usado em combinações como:
- Shells orquestradores (Single-SPA, Module Federation, Zephyr)
- Feature Flags dinâmicas baseadas em AI
- Edge Functions (Cloudflare Workers, Vercel Functions)
- Streams de eventos (Redis Streams, Kafka, RabbitMQ)
Vantagens do SSE hoje
Simplicidade e compatibilidade
- ✅ Baseado em HTTP, sem handshakes adicionais
- ✅ Compatível com infraestruturas modernas (HTTP/2, CDN, Serverless)
- ✅ Código leve, sem dependências externas
- ✅ Reconexão automática nativa (EventSource)
Performance e escalabilidade
- ✅ Ideal para notificações, logs, atualizações contextuais
- ✅ Baixo overhead de conexão
- ✅ Suporte nativo a HTTP/2 multiplexing
- ✅ Cache-friendly com CDNs
Aplicações em arquiteturas de micro frontends
Em estruturas com MFEs desacoplados, o SSE pode ser mantido no Shell e os eventos propagados aos MFEs por meio de CustomEvent. Assim os times continuam isolados e a reatividade em tempo real se mantém.
Exemplo com Single-SPA
// Shell (root-app)
const source = new EventSource('/api/sse');
source.onmessage = ({ data }) => {
const event = JSON.parse(data);
window.dispatchEvent(new CustomEvent('sse:event', { detail: event }));
};
// MFE (qualquer framework)
useEffect(() => {
const handler = (e) => handleIncomingData(e.detail);
window.addEventListener('sse:event', handler);
return () => window.removeEventListener('sse:event', handler);
}, []); Exemplos de uso
1. Atualização de feature flags em tempo real
// Shell - Feature Flags dinâmicas
const featureFlagsSource = new EventSource('/api/feature-flags/sse');
featureFlagsSource.onmessage = ({ data }) => {
const flags = JSON.parse(data);
// Broadcast para todos os MFEs
window.dispatchEvent(new CustomEvent('feature-flags:update', {
detail: flags
}));
};
// MFE - Consumir feature flags
useEffect(() => {
const handleFeatureFlags = (event) => {
const flags = event.detail;
setFeatureFlags(flags);
// Renderização condicional baseada em flags
if (flags.newCheckoutFlow) {
renderNewCheckout();
}
};
window.addEventListener('feature-flags:update', handleFeatureFlags);
return () => window.removeEventListener('feature-flags:update', handleFeatureFlags);
}, []); 2. Notificações de deploy para atualizar MFEs
// Shell - Monitoramento de deploys
const deploySource = new EventSource('/api/deployments/sse');
deploySource.onmessage = ({ data }) => {
const deployment = JSON.parse(data);
if (deployment.status === 'completed') {
// Notificar MFEs sobre nova versão disponível
window.dispatchEvent(new CustomEvent('deployment:completed', {
detail: {
mfe: deployment.mfe,
version: deployment.version,
timestamp: deployment.timestamp
}
}));
}
};
// MFE - Atualização automática
useEffect(() => {
const handleDeployment = (event) => {
const { mfe, version } = event.detail;
if (mfe === 'current-mfe') {
// Mostrar notificação de atualização
showUpdateNotification(version);
}
};
window.addEventListener('deployment:completed', handleDeployment);
return () => window.removeEventListener('deployment:completed', handleDeployment);
}, []); 3. Log de ações em sistemas multitenant
// Shell - Logs de auditoria
const auditSource = new EventSource('/api/audit/sse');
auditSource.onmessage = ({ data }) => {
const auditLog = JSON.parse(data);
// Filtrar por tenant atual
if (auditLog.tenantId === getCurrentTenant()) {
window.dispatchEvent(new CustomEvent('audit:log', {
detail: auditLog
}));
}
};
// MFE - Exibir logs em tempo real
useEffect(() => {
const handleAuditLog = (event) => {
const log = event.detail;
// Adicionar ao feed de atividades
addToActivityFeed({
user: log.user,
action: log.action,
timestamp: log.timestamp,
details: log.details
});
};
window.addEventListener('audit:log', handleAuditLog);
return () => window.removeEventListener('audit:log', handleAuditLog);
}, []); 4. Monitoramento de estado por usuário
// Shell - Estados de sessão
const sessionSource = new EventSource('/api/session/sse');
sessionSource.onmessage = ({ data }) => {
const sessionUpdate = JSON.parse(data);
// Atualizar estado global da sessão
window.dispatchEvent(new CustomEvent('session:update', {
detail: sessionUpdate
}));
};
// MFE - Sincronização de estado
useEffect(() => {
const handleSessionUpdate = (event) => {
const session = event.detail;
// Sincronizar estado local com servidor
setUserStatus(session.status);
setLastActivity(session.lastActivity);
// Renderização condicional baseada em permissão
if (session.permissions.includes('admin')) {
showAdminPanel();
}
};
window.addEventListener('session:update', handleSessionUpdate);
return () => window.removeEventListener('session:update', handleSessionUpdate);
}, []); Comparativo técnico (2025)
| Tecnologia | Indicado para | Considerações |
|---|---|---|
| SSE | Stream unidirecional leve e confiável | Sem suporte a binários |
| WebSocket | Bidirecional em tempo real | Requer mais infraestrutura e estado |
| WebTransport | Baixa latência sobre HTTP/3 | Suporte ainda limitado nos browsers |
| Polling | Simplicidade extrema | Ineficiente e pouco reativo |
Implementação avançada
Classe SSE Manager
class SSEManager {
constructor(baseUrl) {
this.baseUrl = baseUrl;
this.connections = new Map();
this.eventHandlers = new Map();
}
connect(endpoint, options = {}) {
const url = `${this.baseUrl}${endpoint}`;
const source = new EventSource(url);
source.onopen = () => {
console.log(`🔗 SSE conectado: ${endpoint}`);
};
source.onmessage = ({ data }) => {
try {
const event = JSON.parse(data);
this.broadcastEvent(endpoint, event);
} catch (error) {
console.error('Erro ao processar evento SSE:', error);
}
};
source.onerror = (error) => {
console.error(`❌ Erro SSE em ${endpoint}:`, error);
};
this.connections.set(endpoint, source);
return source;
}
broadcastEvent(endpoint, data) {
const eventName = `sse:${endpoint.replace('/', ':')}`;
window.dispatchEvent(new CustomEvent(eventName, { detail: data }));
}
disconnect(endpoint) {
const source = this.connections.get(endpoint);
if (source) {
source.close();
this.connections.delete(endpoint);
}
}
disconnectAll() {
this.connections.forEach((source, endpoint) => {
source.close();
});
this.connections.clear();
}
}
// Uso global
window.sseManager = new SSEManager('/api'); Hook React personalizado
import { useEffect, useState } from 'react';
export function useSSE(endpoint, options = {}) {
const [data, setData] = useState(null);
const [isConnected, setIsConnected] = useState(false);
const [error, setError] = useState(null);
useEffect(() => {
if (!window.sseManager) {
setError('SSE Manager não inicializado');
return;
}
const eventName = `sse:${endpoint.replace('/', ':')}`;
const handleEvent = (event) => {
setData(event.detail);
setIsConnected(true);
setError(null);
};
const handleError = () => {
setIsConnected(false);
setError('Erro na conexão SSE');
};
// Conectar ao endpoint
const source = window.sseManager.connect(endpoint);
// Configurar listeners
window.addEventListener(eventName, handleEvent);
source.addEventListener('error', handleError);
return () => {
window.removeEventListener(eventName, handleEvent);
source.removeEventListener('error', handleError);
window.sseManager.disconnect(endpoint);
};
}, [endpoint]);
return { data, isConnected, error };
} Integração com observabilidade
Logging com Last-Event-ID
// Suporte a reconexão com Last-Event-ID
const source = new EventSource('/api/sse', {
withCredentials: true
});
let lastEventId = null;
source.onmessage = ({ data, lastEventId: eventId }) => {
lastEventId = eventId;
// Log para observabilidade
console.log('SSE Event:', {
data: JSON.parse(data),
eventId,
timestamp: new Date().toISOString()
});
};
// Reconexão com Last-Event-ID
source.addEventListener('error', () => {
setTimeout(() => {
const newSource = new EventSource(`/api/sse?lastEventId=${lastEventId}`);
// ... reconectar
}, 5000);
}); Monitoramento com Datadog/New Relic
// Métricas customizadas
function trackSSEMetrics(endpoint, eventType, latency) {
// Datadog
if (window.DD_RUM) {
window.DD_RUM.addAction('sse_event', {
endpoint,
eventType,
latency
});
}
// New Relic
if (window.newrelic) {
window.newrelic.addPageAction('sse_event', {
endpoint,
eventType,
latency
});
}
} Benefícios em produção
Para microfrontends
- ✅ Comunicação desacoplada entre Shell e MFEs
- ✅ Reatividade em tempo real sem complexidade
- ✅ Isolamento de responsabilidades mantido
- ✅ Escalabilidade horizontal facilitada
Para infraestrutura
- ✅ Baixo custo operacional comparado ao WebSocket
- ✅ Compatibilidade com CDNs e edge computing
- ✅ Suporte nativo a HTTP/2
- ✅ Facilita auditoria e rastreabilidade
Para desenvolvedores
- ✅ Implementação simples sem dependências
- ✅ Debugging facilitado com logs nativos
- ✅ Fallback automático para reconexão
- ✅ Integração fácil com observabilidade
Referências e materiais recomendados
- MDN Web Docs – Server-Sent Events
- WHATWG – EventSource Spec
- Cloudflare Workers – SSE Implementation
- GitHub – SSE bridge com Kafka
Conclusão
Em 2025, o SSE combina com a busca por arquiteturas desacopladas, reativas e de baixo custo operacional. Quando a comunicação bidirecional não é necessária, o SSE é uma solução leve, resiliente e fácil de auditar, principalmente em ambientes com vários MFEs e shell centralizado.
Quando usar SSE
- ✅ Notificações em tempo real
- ✅ Feature flags dinâmicas
- ✅ Logs de auditoria em tempo real
- ✅ Atualizações de estado unidirecionais
- ✅ Monitoramento de sistemas
Quando evitar SSE
- ❌ Comunicação bidirecional necessária
- ❌ Dados binários para transmitir
- ❌ Latência ultra-baixa crítica
- ❌ Browsers muito antigos sem suporte
Este artigo apresenta uma solução prática para reatividade em tempo real em arquiteturas de microfrontends. Para discussões técnicas, implementações ou dúvidas sobre SSE e comunicação entre MFEs, me encontre no LinkedIn ou GitHub.