Versionamento de Eventos

Como evoluir schemas de eventos em sistemas orientados a eventos — compatibilidade backward/forward, upcasting e schema registry.

O desafio do versionamento de eventos

Em uma arquitetura orientada a eventos, os eventos são o contrato entre produtores e consumidores. Diferente das APIs REST, em que o consumidor faz um request e recebe uma resposta imediata, os eventos são publicados e podem ser consumidos por múltiplos serviços — inclusive serviços que não existiam quando o evento foi definido.

Isso torna o versionamento de eventos mais complexo do que o de APIs: você não pode simplesmente “depreciar” um evento, porque pode haver consumidores que reprocessam eventos históricos do log.

Evolução de schemas

Mudanças compatíveis (safe changes)

Essas mudanças não quebram consumidores existentes:

  • Adicionar campos opcionais: Os consumidores que não conhecem o campo simplesmente o ignoram
  • Adicionar novos tipos de eventos: Os consumidores que não os reconhecem os descartam
  • Ampliar intervalos de valores: Por exemplo, aceitar mais valores em um enum (se os consumidores lidarem com valores desconhecidos)

Mudanças incompatíveis (breaking changes)

Essas mudanças exigem uma estratégia de migração:

  • Remover campos: Consumidores que dependem do campo vão falhar
  • Renomear campos: Equivale a remover um e adicionar outro
  • Mudar tipos de dados: Um consumidor que espera uma string e recebe um number vai falhar
  • Mudar a semântica: O campo existe, mas passa a significar algo diferente

Compatibilidade backward e forward

Backward compatibility (leitores novos, escritores antigos)

Um schema é backward compatible se os consumidores com o schema novo conseguem ler eventos produzidos com o schema antigo. Isso é alcançado quando:

  • Os campos novos têm valores padrão
  • Não são removidos campos que os consumidores novos esperam

Forward compatibility (leitores antigos, escritores novos)

Um schema é forward compatible se os consumidores com o schema antigo conseguem ler eventos produzidos com o schema novo. Isso é alcançado quando:

  • Os consumidores ignoram campos desconhecidos
  • Não são alterados os tipos dos campos existentes

Full compatibility

Um schema é fully compatible quando é ao mesmo tempo backward e forward compatible. Esse é o nível mais seguro e o recomendado para a maioria dos casos.

Event upcasting

O upcasting é uma técnica para transformar eventos de uma versão antiga para uma versão nova em tempo de leitura. Em vez de migrar todos os eventos armazenados, aplica-se uma transformação no momento em que são lidos:

Evento v1 armazenado → Upcaster v1→v2 → Consumidor recebe formato v2

Como funciona

  1. Os eventos são armazenados em seu formato original (imutáveis)
  2. Ao ler, é aplicada uma cadeia de upcasters de acordo com a versão do evento
  3. O consumidor sempre recebe o formato mais recente

Vantagens do upcasting

  • Não exige migração massiva de dados
  • Os eventos originais são preservados (audit trail intacto)
  • Novas transformações podem ser adicionadas incrementalmente

Considerações

  • A cadeia de upcasters pode crescer com o tempo (v1→v2→v3→v4)
  • Cada upcaster deve ser determinístico e sem efeitos colaterais
  • Vale a pena testar a cadeia completa de transformações

Schema Registry

Um Schema Registry é um serviço centralizado que armazena e gerencia os schemas de todos os eventos do sistema. Ferramentas como Confluent Schema Registry ou AWS Glue Schema Registry oferecem:

Funcionalidades-chave

  • Armazenamento centralizado: Todos os schemas em um só lugar
  • Validação de compatibilidade: Rejeita schemas novos que quebrem a compatibilidade configurada
  • Evolução controlada: Permite definir regras de compatibilidade por topic/tipo de evento
  • Serialização/deserialização: Produtores e consumidores usam o registry para serializar e deserializar eventos

Níveis de compatibilidade configuráveis

NívelDescrição
BACKWARDConsumidores novos conseguem ler eventos antigos
FORWARDConsumidores antigos conseguem ler eventos novos
FULLBackward + Forward
NONESem validação de compatibilidade

Fluxo típico

  1. O produtor registra um schema novo no registry
  2. O registry valida a compatibilidade com a versão anterior
  3. Se for compatível, atribui um ID ao schema
  4. O produtor serializa o evento com o schema ID
  5. O consumidor usa o schema ID para deserializar

Boas práticas

  • Inclua sempre um campo de versão nos seus eventos (schemaVersion, eventVersion)
  • Use formatos que suportem evolução — Avro, Protobuf ou JSON Schema com regras claras
  • Configure compatibilidade FULL como padrão no seu schema registry
  • Projete consumidores tolerantes — que ignorem campos desconhecidos e lidem com campos ausentes usando valores padrão
  • Teste a compatibilidade antes de fazer deploy de mudanças de schema — idealmente no pipeline de CI/CD
  • Documente cada versão do schema com seu changelog e data de introdução