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
- Os eventos são armazenados em seu formato original (imutáveis)
- Ao ler, é aplicada uma cadeia de upcasters de acordo com a versão do evento
- 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ível | Descrição |
|---|---|
| BACKWARD | Consumidores novos conseguem ler eventos antigos |
| FORWARD | Consumidores antigos conseguem ler eventos novos |
| FULL | Backward + Forward |
| NONE | Sem validação de compatibilidade |
Fluxo típico
- O produtor registra um schema novo no registry
- O registry valida a compatibilidade com a versão anterior
- Se for compatível, atribui um ID ao schema
- O produtor serializa o evento com o schema ID
- 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