In a microservices architecture, each service usually owns its own database. This design keeps services independent and scalable. But problems arise when a business operation needs to span across multiple services.

For example, placing an order might involve:

  • OrderService to create the order
  • InventoryService to reduce stock
  • PaymentService to charge the customer

This kind of workflow creates what’s called a distributed transaction, and managing it consistently is one of the biggest challenges in microservices.

What Is a Distributed Transaction?

A distributed transaction involves multiple operations across different services or databases that need to succeed or fail together. In a traditional system, this might be easy to manage. But in a microservices setup, where data is distributed and services are autonomous, it gets more complicated.

Why Not Use Two-Phase Commit (2PC)?

Two-Phase Commit (2PC) is a classic way to coordinate distributed transactions. It tries to ensure that either all operations succeed or none do. But in microservices, 2PC is rarely used because:

  • It adds significant complexity
  • Many modern tools (like NoSQL databases and message brokers) do not support it
  • It can hurt performance and availability, especially in high-scale systems

So instead of strict consistency, most microservices systems aim for eventual consistency using patterns like Saga and Outbox.

Outbox Pattern: Reliable Event Publishing

The Outbox pattern helps services publish events reliably without losing messages. Here’s how it works:

  1. A service writes to its local database and stores an event in an “outbox” table in the same transaction
  2. A background process or message relay picks up the event and publishes it to a message broker
  3. If the system crashes before the message is sent, the outbox process will retry

Why this works:

  • You avoid partial failures (data saved but message lost)
  • You guarantee that business changes and related events are in sync
  • It’s simple and uses local database transactions only

Saga Pattern: Coordinating Multiple Services

The Saga pattern is used when a business process involves multiple steps across different services. Each step is a local transaction, and if something fails, a compensating action is triggered to undo the change. For example:

  1. PaymentService charges the customer
  2. InventoryService reserves the product
  3. ShippingService starts the delivery

If step 3 fails, the saga can trigger compensations like:

  • Undo the inventory reservation
  • Refund the payment

Sagas can be implemented in two ways:

  • Choreography: Services listen to and react to events from other services
  • Orchestration: A central coordinator service tells others what to do next

Summary

Distributed transactions in microservices are hard to get right. Instead of trying to keep everything tightly consistent, most systems go for eventual consistency using patterns like:

  • Outbox for safe and reliable messaging
  • Saga for coordinating workflows that span multiple services

These approaches reduce complexity, improve scalability, and let services remain independent while still working together effectively.