DEV Community

myougaTheAxo
myougaTheAxo

Posted on

Design Transactional Outbox Pattern with Claude Code: Reliable Event Delivery, CDC

Introduction

'Saved to DB but event never fired' — Transactional Outbox combines business logic write and event delivery in the same transaction for guaranteed delivery. Let Claude Code design this.

CLAUDE.md Rules

## Outbox Pattern Rules
- Outbox write in same transaction as business logic
- At-least-once delivery (idempotency on consumer)
- FOR UPDATE SKIP LOCKED for safe batch processing
- Debezium CDC: WAL-based real-time delivery
Enter fullscreen mode Exit fullscreen mode

Generated Implementation

// Business logic + outbox in same transaction
export async function createOrderWithEvent(data: CreateOrderInput) {
  return prisma.$transaction(async (tx) => {
    const order = await tx.order.create({ data: { userId: data.userId } });

    await tx.outboxEvent.create({
      data: {
        aggregateType: 'Order',
        aggregateId: order.id,
        eventType: 'OrderCreated',
        payload: { orderId: order.id, userId: data.userId },
        status: 'pending',
      },
    });

    return order;
  });
}

// Idempotent consumer
async function handleEvent(event: OutboxMessage) {
  const processed = await prisma.processedEvent.findUnique({ where: { eventId: event.eventId } });
  if (processed) return;

  await processBusinessLogic(event);
  await prisma.processedEvent.create({ data: { eventId: event.eventId } });
}
Enter fullscreen mode Exit fullscreen mode

Summary

  1. Same $transaction for business logic + outbox: DB commit = guaranteed delivery
  2. FOR UPDATE SKIP LOCKED: multiple workers process safely without conflicts
  3. Debezium CDC: WAL-based real-time to Kafka (no polling delay)
  4. Idempotent consumer: processedEvent table prevents duplicate processing

Review with **Code Review Pack (¥980)* at prompt-works.jp*

myouga (@myougatheaxo) — Axolotl VTuber.

Top comments (0)