Day 44: Exactly Once in a Microservices Flow

Advanced Java for Banking and Ledger Systems ~9 min read 60 min hands-on

Welcome back, future architects of financial truth!

Today, we're diving into one of the most misunderstood yet critical concepts in distributed systems, especially in banking and ledger technologies: "Exactly Once" delivery. If you've ever heard someone casually say, "We just need exactly-once messaging," you know they're either new to the game or haven't truly wrestled with the beast. In the world of 100 million requests per second, where every cent must be accounted for, getting this right isn't just good practiceβ€”it's the difference between a compliant, solvent system and a financial catastrophe.

Agenda: Dispelling Myths and Building Robustness

  1. The "Exactly Once" Illusion: Why true "exactly once" delivery is a unicorn in distributed systems.

  2. The Practical Reality: How we achieve effective exactly-once processing: "at least once delivery + idempotent processing."

  3. The Transaction Outbox Pattern: The bedrock for reliable event publishing in microservices.

  4. Idempotency Keys: Your shield against duplicate processing.

  5. Hands-on Implementation: Building a payment processing flow with an outbox and idempotent ledger updates.

Core Concepts: The Truth About "Exactly Once"

Let's cut straight to it: In a distributed system, achieving true "exactly once delivery" of a message from point A to point B is practically impossible. Networks fail, services crash, and disks corrupt. Any system promising "exactly once" usually means "effectively exactly once" through a combination of "at least once" delivery and idempotent processing at the consumer.

Think about it:

  • At Most Once: A message might be sent, but if the sender crashes before confirmation, it's never retried. Data loss. Unacceptable for banking.

  • At Least Once: A message is guaranteed to be delivered, but it might be delivered multiple times if acknowledgments are lost or timeouts occur. This is the foundation we build on.

  • Effectively Exactly Once: This is the holy grail. We accept "at least once" delivery from our message brokers (like Kafka), and then we design our consumers to handle duplicates gracefully, ensuring the effect of processing a message only happens once.

Architecture: A Financial Transaction Flow

Imagine a simple payment scenario:

  1. A user initiates a payment via the Payment Service.

  2. The Payment Service processes the payment and needs to update the Ledger Service to record the transaction.

  3. The Ledger Service must ensure that even if it receives the same payment event multiple times, the ledger entry is recorded only once.

Our architecture will involve:

  • Payment Service: Handles payment initiation, persists payment details, and records an "outbox" event.

  • Outbox Event Publisher (within Payment Service): A component that reliably reads events from the outbox table and publishes them to a message broker.

  • Message Broker (e.g., Kafka): A durable, fault-tolerant system for transmitting events. (For our hands-on, we'll simulate this for simplicity).

  • Ledger Service: Consumes events from the message broker and applies them to the ledger, ensuring idempotency.

  • Databases: Each service has its own database.

The Transaction Outbox Pattern: Your Reliability Engine

This pattern is non-negotiable for high-integrity systems. Instead of directly publishing an event to a message broker after a database transaction, you perform two operations within a single local database transaction:

  1. Persist your business entity change (e.g., save the Payment record).

  2. Persist an OutboxEvent record to a dedicated outbox table (containing the event payload and metadata).

If the local transaction succeeds, both are committed. If it fails, both are rolled back. This ensures atomicity: either the payment is saved and the event is queued for publishing, or neither happens.

Keep going with the full lesson

The rest of this lesson β€” plus its implementation guide, runnable scripts and source code β€” is included with the course or an All Access plan. Create a free account to start the free lessons in every course.

Already a member? Sign in