Voltar ao Blog
    Microfrontends

    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.

    21 de junho de 20257 min

    O Server-Sent Events (SSE) continua sendo uma opção sólida para fluxos de dados unidirecionais no frontend — especialmente em contextos modernos de Micro Frontends (MFEs), onde simplicidade, performance e escalabilidade são essenciais.

    Hoje, o SSE vem sendo utilizado 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 no Cenário Atual

    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, mantendo o isolamento dos times e a reatividade em tempo real.

    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 Práticos

    1. Atualização em Tempo Real de Feature Flags

    // 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 Atualização de 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 Estados 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 (Cenário 2025)

    TecnologiaIndicado paraConsiderações
    SSEStream unidirecional leve e confiávelSem suporte a binários
    WebSocketBidirecional em tempo realRequer mais infraestrutura e estado
    WebTransportBaixa latência sobre HTTP/3Suporte ainda limitado nos browsers
    PollingSimplicidade extremaIneficiente 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

    🎯 Conclusão

    O uso de SSE em 2025 se mostra coerente 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 facilmente auditável — especialmente em ambientes com múltiplos 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.