ADR-017: la configuración de cada tópico vive en el contrato AsyncAPI
Aceptado el 10/11/2026 por Andrés.
Contexto
El contrato contracts/asyncapi/eventos.v1.yaml declara los canales, los mensajes y sus esquemas, pero no las propiedades operativas de cada tópico: particiones, réplicas, política de limpieza (compactado o por tiempo) y retención. Hoy esos valores viven en una tabla de docs/to-be/06-eventos.md y el carril f3-plantillas los transcribió a mano en scripts/topics-plan.js para crear los tópicos en local.
Eso deja dos fuentes que pueden divergir. Además el contrato no declara:
- los tópicos crudos de los adaptadores (
raw.erp.changes,raw.wms.changes); - los tópicos de reintento y DLQ (Dead Letter Queue, cola donde terminan los mensajes que fallaron todos sus reintentos) que pide
06-eventos.md:<tópico>.<grupo>.retryy<tópico>.<grupo>.dlqpor cada grupo consumidor; - las particiones de
andes.oms.events.v1yandes.audit.v1, que ningún documento fija.
Un tópico mal configurado no se nota en una revisión de código y sí en producción: un tópico de estado sin compactar pierde la capacidad de reconstruir Redis (05-modelo-de-lectura.md), y una DLQ que nadie declaró no existe cuando hace falta.
Opciones
| Opción | Fuente de verdad | A favor | En contra |
|---|---|---|---|
| A. Bindings de Kafka en el AsyncAPI | El contrato | Una sola fuente para qué viaja y cómo se guarda; la CLI oficial lo valida; consumidores y revisores lo ven junto al evento | Mezcla datos operativos con el contrato; las diferencias por ambiente se resuelven fuera |
| B. Dejarlo como hoy | Tabla en 06-eventos.md más script |
Sin trabajo | Dos fuentes; nada valida que coincidan |
C. Archivo propio, por ejemplo topics.yaml |
Un tercer formato | Separado del contrato | Una fuente más, sin validador estándar |
| D. OpenTofu como fuente | El proveedor de Confluent en infra/tofu |
Es lo que crea los tópicos en la nube (F7) | No sirve en local; el consumidor no lo ve; ata el contrato a un proveedor |
| E. Recursos de un operador de Kafka en Kubernetes | Manifiestos del operador | Declarativo | F7 usa Confluent Cloud, no un operador; no sirve en compose |
Decisión
Opción A, con los bindings de Kafka de AsyncAPI en su versión 0.5.0 (la vigente, verificada el 10/11/2026 en el repositorio oficial asyncapi/bindings):
- Cada canal declara
bindings.kafkaconpartitions,replicasytopicConfiguration(cleanup.policy,retention.msy, donde aplique,max.message.bytes). - Cada operación de recepción declara su
groupIden el binding de operación. Los canales de reintento y DLQ se declaran como canales del contrato, uno por grupo que los necesite, con la convención de06-eventos.md. - Los tópicos crudos de los adaptadores se declaran como canales internos.
replicaslleva el valor de producción (3, conmin.insync.replicas=2). En local la herramienta usa 1 y lo dice en su salida; es la única diferencia por ambiente.scripts/topics-plan.jsdeja de traer valores transcritos: lee solo el contrato y falla si un canal no tiene sus bindings. El mismo plan alimenta en F7 la creación de tópicos con OpenTofu.
Por qué el contrato y no otra fuente: CLAUDE.md pone los contratos primero, y el AsyncAPI ya es lo que leen productores y consumidores. Si la retención o la compactación de un tópico cambia lo que un consumidor puede hacer (releer desde cero, por ejemplo), es parte del contrato, no un detalle de infraestructura.
Verificación
make lint-asyncapivalida el contrato con los bindings (la CLI oficial confirma que la versión 0.5.0 de los bindings es compatible con AsyncAPI 3.0.0; si no lo es, este ADR se revisa).make topics-test: los tópicos creados coinciden con el contrato, incluidos reintento y DLQ.- Un verificador falla si un canal no declara
bindings.kafka.
Qué se sacrifica
- El contrato crece con datos operativos y cambia más seguido (una retención ajustada es un cambio de contrato).
topicConfigurationadmite propiedades propias de Confluent (validación de esquema); no se usan, para no atar el contrato a un proveedor.- Las diferencias por ambiente distintas de
replicasno se expresan en el contrato; si aparecen, se agregan a la herramienta y se documentan aquí.