Versionado de Eventos

Cómo evolucionar schemas de eventos en sistemas event-driven — compatibilidad backward/forward, upcasting y schema registry.

El desafío del versionado de eventos

En una arquitectura event-driven, los eventos son el contrato entre productores y consumidores. A diferencia de las APIs REST donde el consumidor hace un request y recibe una respuesta inmediata, los eventos se publican y pueden ser consumidos por múltiples servicios — incluso servicios que no existían cuando el evento fue definido.

Esto hace que el versionado de eventos sea más complejo que el de APIs: no podés simplemente “deprecar” un evento porque puede haber consumidores que reprocesan eventos históricos del log.

Evolución de schemas

Cambios compatibles (safe changes)

Estos cambios no rompen consumidores existentes:

  • Agregar campos opcionales: Los consumidores que no conocen el campo lo ignoran
  • Agregar nuevos tipos de eventos: Los consumidores que no los reconocen los descartan
  • Ampliar rangos de valores: Por ejemplo, aceptar más valores en un enum (si los consumidores manejan valores desconocidos)

Cambios incompatibles (breaking changes)

Estos cambios requieren una estrategia de migración:

  • Eliminar campos: Consumidores que dependen del campo fallarán
  • Renombrar campos: Equivalente a eliminar uno y agregar otro
  • Cambiar tipos de datos: Un consumidor que espera un string y recibe un number fallará
  • Cambiar la semántica: El campo existe pero significa algo diferente

Compatibilidad backward y forward

Backward compatibility (lectores nuevos, escritores viejos)

Un schema es backward compatible si los consumidores con el schema nuevo pueden leer eventos producidos con el schema viejo. Esto se logra cuando:

  • Los campos nuevos tienen valores por defecto
  • No se eliminan campos que los consumidores nuevos esperan

Forward compatibility (lectores viejos, escritores nuevos)

Un schema es forward compatible si los consumidores con el schema viejo pueden leer eventos producidos con el schema nuevo. Esto se logra cuando:

  • Los consumidores ignoran campos desconocidos
  • No se cambian los tipos de campos existentes

Full compatibility

Un schema es fully compatible cuando es tanto backward como forward compatible. Este es el nivel más seguro y el recomendado para la mayoría de los casos.

Event upcasting

El upcasting es una técnica para transformar eventos de una versión antigua a una versión nueva en tiempo de lectura. En lugar de migrar todos los eventos almacenados, se aplica una transformación cuando se leen:

Evento v1 almacenado → Upcaster v1→v2 → Consumidor recibe formato v2

Cómo funciona

  1. Los eventos se almacenan en su formato original (inmutables)
  2. Al leer, se aplica una cadena de upcasters según la versión del evento
  3. El consumidor siempre recibe el formato más reciente

Ventajas del upcasting

  • No requiere migración masiva de datos
  • Los eventos originales se preservan (audit trail intacto)
  • Se pueden agregar nuevas transformaciones incrementalmente

Consideraciones

  • La cadena de upcasters puede crecer con el tiempo (v1→v2→v3→v4)
  • Cada upcaster debe ser determinista y sin efectos secundarios
  • Conviene testear la cadena completa de transformaciones

Schema Registry

Un Schema Registry es un servicio centralizado que almacena y gestiona los schemas de todos los eventos del sistema. Herramientas como Confluent Schema Registry o AWS Glue Schema Registry ofrecen:

Funcionalidades clave

  • Almacenamiento centralizado: Todos los schemas en un solo lugar
  • Validación de compatibilidad: Rechaza schemas nuevos que rompan la compatibilidad configurada
  • Evolución controlada: Permite definir reglas de compatibilidad por topic/tipo de evento
  • Serialización/deserialización: Los productores y consumidores usan el registry para serializar y deserializar eventos

Niveles de compatibilidad configurables

NivelDescripción
BACKWARDConsumidores nuevos pueden leer eventos viejos
FORWARDConsumidores viejos pueden leer eventos nuevos
FULLBackward + Forward
NONESin validación de compatibilidad

Flujo típico

  1. El productor registra un schema nuevo en el registry
  2. El registry valida la compatibilidad con la versión anterior
  3. Si es compatible, asigna un ID al schema
  4. El productor serializa el evento con el schema ID
  5. El consumidor usa el schema ID para deserializar

Buenas prácticas

  • Incluí siempre un campo de versión en tus eventos (schemaVersion, eventVersion)
  • Usá formatos que soporten evolución — Avro, Protobuf o JSON Schema con reglas claras
  • Configurá compatibilidad FULL como default en tu schema registry
  • Diseñá consumidores tolerantes — que ignoren campos desconocidos y manejen campos faltantes con defaults
  • Testeá la compatibilidad antes de deployar cambios de schema — idealmente en el pipeline de CI/CD
  • Documentá cada versión del schema con su changelog y fecha de introducción