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