0019. Versionado de eventos de integración (outbox / Kafka)¶
Estado: Propuesto Fecha: 2026-09-08 Autor: Duval Alcivar Módulo(s) afectado(s): svc-identidad, consumidores de eventos (ERP, CRM)
Contexto¶
El alta de empresa (0008) avisa a los demás sistemas con un evento
(empresa.creada, sucursal.creada) vía outbox; hoy se entrega por HTTP y
mañana por Kafka, "sin cambiar el mensaje". Pero un mensaje que crece con los
meses va a cambiar de contenido (se agrega un campo, se renombra otro), y sin
versión los consumidores no saben qué contrato están leyendo → roturas en
caliente y acoplamiento tácito. Falta la regla de versionado que aplica la
misma disciplina del contrato de API (transversal 0016) a los eventos.
Decisión¶
Todo evento publicado por svc-identidad lleva versión explícita desde hoy
(empresa.creada.v1), un payload validado contra JSON Schema versionado en
el repositorio, y reglas de compatibilidad idénticas a las de la API
(transversal 0016).
1. Nombre del evento = contrato versionado¶
<entidad>.<verbo>.vN ejemplo: empresa.creada.v1, sucursal.creada.v1
La versión siempre es explícita desde el primer evento (nada de "v1 implícito y se agrega v2 cuando rompa"): los consumidores parsean y enrutan con una regla uniforme, y el nombre del tópico no cambia cuando cambia el contrato.
El envelope se mantiene como lo definió el 0008 (el campo evento actualizado
con el sufijo .vN, más trace_id y payload), y se añade dataversion por
si un mismo transporte lleva varias versiones:
{
"evento": "empresa.creada.v1", // nombre + versión
"dataversion": "1",
"trace_id": "<uuid>",
"payload": {
"empresa_id": 1,
"nombre": "Empresa Demo",
"identificacion": "0999999999001",
"sucursales": [],
"aplicaciones": ["erp", "crm"]
}
}
2. Schemas versionados en el repo¶
Cada evento versionado tiene su JSON Schema en el repositorio:
schemas/events/empresa.creada.v1.schema.json. La publicación valida el
payload contra el schema (fallo de validación = evento no se publica) y
los consumidores pueden validar también al recibir. El CI valida los schemas
contra openapi-spec-validator-equivalente para JSON Schema (mismo espíritu del
transversal 0017 aplicado a eventos).
3. Reglas de compatibilidad (mismas que la API)¶
Cambio en payload |
¿Rompe? | Acción |
|---|---|---|
Añadir campo opcional (con default) |
no | se documenta, misma versión |
| Añadir campo requerido | sí | nueva versión (empresa.creada.v2) |
| Renombrar / cambiar tipo / quitar campo | sí | nueva versión |
| Cambiar semántica (ej. cambio de moneda de un monto) | sí | nueva versión |
4. Coexistencia y retiro¶
- Una versión nueva no reemplaza la vieja de golpe: ambos se publican
durante la transición (productor emite
v2y sigue emitiendov1mientras haya consumidores pendientes), igual que la deprecación de la API (0016). - Se retira
v1cuando todos los consumidores migraron (métrica de consumo en 0 durante la ventana), y se anota su estadodeprecated→retireden un registro de versiones junto a los schemas. - Cada versión queda como archivo en
schemas/events/para auditoría; nunca se sobrescribe un schema histórico.
5. Cuando llegue Kafka¶
- Tópico por entidad:
identidad.empresa(no uno por versión). key=empresa_id(orden por entidad, partición estable por empresa).value= el envelope del §1; los consumidores filtran porevento(nombre + versión).- El canal cambia (HTTP → Kafka), el contrato no: por eso se versiona ya.
Alternativas descartadas¶
- Avro + Schema Registry (Confluent) — potente pero impone toolchain y compat-broker extra; JSON Schema ya es el estándar del proyecto (transversal 0017) y suficiente para nuestro volumen de eventos.
- Protobuf — igual que Avro; añade generadores de código en cada consumidor sin ganancia para eventos lentos (onboarding de empresas).
- Un tópico/endpoint por versión (
empresa.creada.v2como tipo aparte) — multiplica el fanout y el enrutamiento; la versión es un atributo del evento, no un canal. - Sin versionado (se edita el payload en el lugar) — los consumidores viejos rompen sin aviso; no hay manera de retirar un contrato.
- Versionar solo en la doc, no en el nombre — sin propiedad explícita el consumidor no puede enrutar ni el CI puede comparar versiones.
Consecuencias¶
empresa.creadapasa aempresa.creada.v1desde ahora; el salto HTTP→Kafka ya no toca el contrato (solo el transporte), como prometió el 0008.- Cualquier cambio de
payloadexige un schema nuevo + validación CI + ventana de coexistencia: la rotura de consumidores pasa a ser un evento raro y planificado. - Los consumidores validan contra los schemas del repo; un evento inválido genera alerta en el productor (no se publica) y en el consumidor (se tacha).
- Queda pendiente (no de svc-identidad): el transversal de mensajería que formalice el mismo reglamento para todos los servicios, y el registro central de versiones con su UI/estado.
Este ADR formaliza la promesa del 0008 (mensaje estable, outbox) y el ciclo de vida de empresa del 0016 con reglas de versionado iguales al 0016 transversal (API). La CI que valida schemas es la extensión del 0017 transversal a eventos.