Distributed transactions coordinate data changes across multiple services or databases. Unlike local @Transactional, they must handle network failures and partial commits.

The Problem

  Order Service ──create order──▶ Order DB ✓
       │
       ├──reserve stock──▶ Inventory Service ──▶ Inventory DB ✓
       │
       └──charge payment──▶ Payment Service ──▶ Payment DB ✗ (failed!)

Order and inventory committed, payment failed → inconsistent state
  

Two-Phase Commit (2PC)

  Coordinator:
  Phase 1 (Prepare): Ask all participants "Can you commit?"
    Order DB: YES → Order DB: YES → Payment DB: YES
  Phase 2 (Commit):  Tell all participants to commit
    Order DB: COMMIT → Order DB: COMMIT → Payment DB: COMMIT
  
Pros Cons
Strong consistency Blocking (locks resources during prepare)
Well understood Coordinator is single point of failure
Poor performance under load
Not suitable for microservices

Java EE: JTA (Java Transaction API) with XA datasources. Rarely used in modern microservices.

A sequence of local transactions, each with a compensating action:

  Create Order → Reserve Stock → Process Payment → Ship Order
     ↓ fail         ↓ fail           ↓ fail
 Cancel Order ← Release Stock ← Refund Payment
  

Choreography (Event-Driven)

Each service listens for events and reacts:

  Order Service:   create order → publish OrderCreated
Inventory Service: listen → reserve stock → publish StockReserved
                                    ↓ fail → publish StockReservationFailed
Payment Service: listen → charge → publish PaymentProcessed
Order Service:   listen StockReservationFailed → cancel order
  

Orchestration (Central Coordinator)

A saga orchestrator directs each step:

  @Service
public class OrderSagaOrchestrator {
    public void createOrder(CreateOrderRequest request) {
        SagaTransaction saga = SagaBuilder.create()
            .step("createOrder", () -> orderService.create(request))
            .compensate("cancelOrder", (order) -> orderService.cancel(order.getId()))
            .step("reserveStock", (order) -> inventoryService.reserve(order))
            .compensate("releaseStock", (order) -> inventoryService.release(order))
            .step("processPayment", (order) -> paymentService.charge(order))
            .compensate("refundPayment", (order) -> paymentService.refund(order))
            .execute();
    }
}
  

Eventual Consistency

Accept temporary inconsistency with guaranteed convergence:

  Time 0: Order created (status: PENDING)
Time 1: Stock reserved (order still PENDING)
Time 2: Payment processed (order → CONFIRMED)
Time 3: All services consistent
  

Clients must handle intermediate states:

  @GetMapping("/orders/{id}")
public OrderStatusResponse getOrderStatus(@PathVariable Long id) {
    Order order = orderService.findById(id);
    return new OrderStatusResponse(order.getId(), order.getStatus(),
        order.getStatus() == PENDING ? "Processing, please wait" : "Complete");
}
  

Outbox Pattern

Ensure reliable event publishing alongside database writes:

  @Transactional
public Order createOrder(CreateOrderRequest request) {
    Order order = orderRepository.save(new Order(request));
    outboxRepository.save(new OutboxEvent("OrderCreated", order.getId(), toJson(order)));
    return order;
}

// Separate process polls outbox and publishes to Kafka
@Scheduled(fixedRate = 1000)
public void publishOutboxEvents() {
    outboxRepository.findUnpublished().forEach(event -> {
        kafkaTemplate.send(event.getTopic(), event.getPayload());
        event.markPublished();
        outboxRepository.save(event);
    });
}
  

Comparison

Pattern Consistency Complexity Performance
2PC Strong High Low
Saga (choreography) Eventual Medium High
Saga (orchestration) Eventual Medium High
Outbox + events Eventual Medium High

Best Practices

  • Avoid 2PC in microservices — use Saga pattern instead
  • Design compensating actions for every step in a saga
  • Use the Outbox pattern to guarantee event delivery
  • Make all saga steps idempotent — retries are inevitable
  • Show intermediate states to users (PENDING, PROCESSING)