Actualizar una orden y publicar su evento son dos acciones distintas. Si la base confirma y el broker falla, puede existir una orden sin aviso. Si el aviso sale primero y la base aborta, otro servicio puede actuar sobre un hecho que nunca quedó confirmado.
Una outbox registra el cambio y el evento pendiente dentro de la misma transacción local. Resuelve esa escritura dual en su origen; no convierte la red, el broker ni el consumidor en participantes de una sola transacción. El relay todavía puede repetir una publicación y una proyección puede estar retrasada.
El evento pertenece al commit
Al cambiar una orden a confirmada, la transacción guarda una fila de outbox con event_id, identidad de entidad, tipo, versión y payload. La identidad es estable durante reintentos. El evento representa un hecho confirmado y contiene lo necesario para sus consumidores, evitando exportar indiscriminadamente campos privados de una tabla.
La nota de 2PC y Saga explica la frontera local. Aquí el foco es qué ocurre después: reclamar trabajo, publicar, reconocer duplicados y reconstruir vistas. La regla crítica es que ningún camino de escritura relevante cambie el negocio sin registrar el evento que exige su contrato.
Relay con recuperación durable
Un relay por polling selecciona eventos pendientes. Varios workers pueden reclamar filas con bloqueos breves; SKIP LOCKED evita esperar por filas ya reclamadas, pero no da una instantánea global ni garantiza orden entre workers. El claim y su propietario se guardan en una transacción corta.
Si el bloqueo se libera antes de llamar al broker, hace falta estado durable: plazo de reclamación, intento y versión de propietario. La caída de un worker permite recuperar el evento; una confirmación tardía sólo modifica el claim que todavía le pertenece. No se mantienen locks de base durante toda la espera de red.
Tras una confirmación de publicación válida se registra el progreso. Puede perderse la respuesta del broker o caer el relay antes de guardar ese registro. El próximo intento conserva event_id y puede publicar de nuevo. Ningún orden de esos dos pasos elimina por sí solo la incertidumbre.
Polling y CDC son mecanismos diferentes
CDC observa cambios confirmados mediante el log de la base y los transforma en eventos. Un conector de outbox puede leer esas inserciones sin que la aplicación consulte continuamente la tabla. La posición del log y la identidad de negocio siguen siendo objetos diferentes.
La elección depende de permisos, retención del log, operación del conector y latencia requerida. Un consumidor detenido puede aumentar lag y uso de disco asociado a datos retenidos; una posición irrecuperable obliga a resincronizar según el contrato. CDC no identifica automáticamente qué cambio técnico debe convertirse en un evento público ni arregla esquemas incompatibles.
--- config: look: handDrawn --- flowchart TD app["Operación de negocio"] --> database[("Commit local<br/>Estado + evento en outbox")] database --> relay["Relay<br/>Claim durable o CDC"] relay -->|"Evento con identidad estable"| broker["Broker<br/>Publicación puede repetirse"] broker -->|"Confirmación de publicación"| relay relay --> progress["Registrar progreso<br/>Puede requerir recuperación"] broker --> consumer["Consumidor<br/>Deduplicar y validar versión"] consumer --> view[("Vista + progreso local<br/>Misma transacción si aplica")]
Las flechas no prometen una confirmación conjunta entre la base, el broker y la vista. Cada frontera declara qué persistió y cómo recupera un resultado desconocido.
Orden y deduplicación no son equivalentes
La idempotencia permite reconocer un evento repetido. Para el orden se necesita además una secuencia o versión por entidad y una estrategia compatible de particionado y procesamiento. Reclamar primero una fila no asegura terminar primero su publicación.
Una proyección del inventario puede recibir versión ocho antes que siete. Si cada evento contiene el estado completo, una actualización condicionada puede conservar la versión más reciente. Si representa incrementos o transiciones dependientes, descartar arbitrariamente la versión anterior puede perder trabajo: debe detectar el hueco, recuperar la secuencia o reconstruir desde una fuente autoritativa.
El broker tampoco garantiza orden global entre entidades. Un feed, un índice de búsqueda y un saldo pueden necesitar contratos distintos. Los patrones de acceso ayudan a definir qué vista se deriva y qué datos siguen siendo autoridad.
Reconstruir una vista sin repetir efectos externos
Una reconstrucción combina una instantánea coherente con una posición de continuación, o reproduce un historial suficiente. El contrato define cómo evitar huecos entre snapshot y eventos y cómo retomar después de una caída. No basta copiar las filas actuales y empezar a consumir «desde ahora» mientras llegan escrituras.
Puede crearse una vista paralela, aplicar los cambios siguientes y verificar versiones, conteos e invariantes antes de cambiar lectores. La transición debe coordinar qué versión sirve cada lector y conservar recuperación. Reproducir eventos para construir una vista no autoriza volver a cobrar, enviar correos ni ejecutar comandos de negocio.
La retención determina qué puede reconstruirse. Si el historial ya no existe, hace falta otra instantánea y reconciliación. El caso de boletaje mantiene inventario autoritativo aunque Elasticsearch o el mapa de asientos estén retrasados.
Retención, mensajes inválidos y verificación
Una outbox no puede crecer indefinidamente. Se distinguen pendientes, publicados y retenidos para auditoría o replay; la limpieza respeta a los consumidores y la ventana de recuperación. Un evento incompatible requiere diagnóstico y una estrategia explícita, no reintentos infinitos ni borrar el registro para que desaparezca el error.
Las pruebas cortan el relay después de publicar y antes de marcar progreso, duplican eventos, invierten versiones y reconstruyen mientras cambian entidades. También dejan vencer claims y entregan confirmaciones tardías. Se observan antigüedad de pendientes, lag, duplicados, huecos y divergencias entre autoridad y proyección.
Una outbox es útil cuando el negocio necesita conservar la intención de publicación con su cambio. Su éxito se mide por recuperación y vistas correctas bajo fallos, no sólo por cuántos eventos salen por segundo.
Referencias: transacciones en PostgreSQL, Outbox Event Router de Debezium y alcance de las garantías de entrega de Kafka.