Jesús Erro
Explorar conexiones
Transacción atómica entre tablas de negocio y Outbox, relay hacia broker y consumidores.

Transactional Outbox explicado desde cero

Integrar sistemas suele plantear una pregunta incómoda: ¿cómo publico un evento solo si la transacción de negocio tuvo éxito? Escribir primero en la base y luego en una cola crea dual-write: si falla el segundo paso, los sistemas divergen. El Transactional Outbox resuelve esto guardando el evento en la misma transacción que el cambio de negocio.

El problema del dual-write

Imagina crear un pedido y notificar al servicio de inventario. Si insertas el pedido y luego publicas en Kafka, pueden ocurrir tres desastres silenciosos:

  1. El pedido se guarda; la publicación falla → inventario desactualizado.
  2. La publicación tiene éxito; el commit falla → inventario decrementado sin pedido.
  3. Reintentos duplican mensajes → consumidores deben ser idempotentes.

El outbox evita elegir cuál desastre prefieres al separar atomicidad local de entrega eventual.

Una transacción, dos escrituras locales

En la misma transacción de base de datos:

  • se actualizan las tablas de negocio (orders, payments, …);
  • se inserta una fila en outbox con event_type, payload, status = NEW.

Si la transacción hace commit, ambas cosas existen. Si aborta, ninguna existe. No hay ventana de inconsistencia local.

La tabla outbox es append-only en la práctica: el relay marca filas como enviadas, no las borra inmediatamente si quieres auditoría.

Relay / worker

Un proceso separado —Outbox Relay— ejecuta un bucle:

  1. Fetch: lee eventos NEW (con bloqueo optimista o FOR UPDATE SKIP LOCKED).
  2. Publish: envía al broker o event stream.
  3. Mark as sent: actualiza el estado a SENT (o equivalente).

Si publish falla, el evento permanece NEW o pasa a FAILED con retry. At-least-once delivery es la expectativa realista; exactly-once end-to-end requiere idempotencia en consumidores.

Broker y consumidores

El diagrama muestra un message broker durable con orden por partición. Los consumidores —Order Service, Inventory Service, Analytics— procesan eventos de forma independiente.

La ilustración usa nombres genéricos. No afirma que este archivo utilice exactamente esos servicios ni ese broker concreto; describe el patrón arquitectónico.

Propiedades que importan

Propiedad Qué aporta
Atomicidad Negocio + evento committed juntos.
Sin dual-write Un solo commit local.
Retries El relay reintenta publish sin reejecutar la lógica de negocio.
At-least-once Duplicados posibles; consumidores idempotentes.
Observabilidad La outbox es audit trail de lo pendiente y lo enviado.
Recuperación Tras caída del relay, el backlog se procesa al reiniciar.

Relación con migraciones

Durante convivencia entre PostgreSQL y Azure SQL, el outbox puede propagar cambios hacia el sistema destino sin acoplar la transacción OLTP a la latencia de sincronización remota. No es la única estrategia —batch y CDC existen— pero encaja cuando necesitas eventos de negocio explícitos con garantías transaccionales.

Límites

Outbox añade latencia (el evento no es instantáneo) y operación del relay (monitorización, dead letters, backpressure). No sustituye diseño de esquema de eventos ni contratos de versionado.

Pero para OLTP que debe notificar downstream con fiabilidad, es uno de los patrones más claros que conozco — y merece entenderse desde cero antes de elegir alternativas más pesadas.