Transactional Outbox
Publica eventos de forma fiable a un message broker desde una base de datos transaccional.
Contexto
Quieres "guardar el pedido y publicar OrderPlaced al broker". Si lo haces en dos pasos, puedes guardar el pedido y caer antes de publicar — o al revés.
Problema
DB y broker son dos recursos independientes. No hay transacción común.
Solución
Guarda el evento en una tabla outbox dentro de la misma transacción que el agregado. Un proceso aparte lee la tabla y publica los eventos pendientes.
#Ejemplo en C#
public class OrderService
{
public async Task PlaceAsync(Order order)
{
await using var tx = await _db.Database.BeginTransactionAsync();
_db.Orders.Add(order);
_db.OutboxMessages.Add(new OutboxMessage {
Type = nameof(OrderPlaced),
Payload = JsonSerializer.Serialize(new OrderPlaced(order.Id))
});
await _db.SaveChangesAsync(); // ambos en la MISMA transacción
await tx.CommitAsync();
}
}
// Worker aparte:
public class OutboxDispatcher : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken ct)
{
while (!ct.IsCancellationRequested)
{
var pending = await _db.OutboxMessages.Where(m => !m.Processed).Take(100).ToListAsync(ct);
foreach (var m in pending)
{
await _bus.PublishAsync(m.Type, m.Payload, ct);
m.Processed = true;
}
await _db.SaveChangesAsync(ct);
await Task.Delay(500, ct);
}
}
}Tradeoffs
| Pro | Contra |
|---|---|
| Garantía at-least-once con datos consistentes | Consumidores deben ser idempotentes |
| No requiere transacción distribuida | Pequeño retardo entre commit y publicación |
#extra #messaging #reliability