How Will You Handle Distributed Transactions in Microservices Architecture?
Distributed transactions are one of the biggest challenges in microservices architecture.
In monolithic applications:
Single Application Single Database Single Transaction
Traditional database transactions work easily using:
BEGIN COMMIT ROLLBACK
But in microservices:
- Each service has its own database
- Each service has independent transactions
- Services communicate asynchronously
So maintaining consistency across services becomes difficult.
Real-Time Production Example
E-Commerce Order Flow
Customer Places Order
↓
Order Service
↓
Payment Service
↓
Inventory Service
↓
Notification Service
Problem Scenario
Suppose:
- Order Service successfully creates order
- Payment Service deducts money
- Inventory Service fails
Now System Becomes Inconsistent
Order Created = YES Payment Deducted = YES Inventory Reserved = NO
Customer money deducted, but order cannot be fulfilled.
Why Two-Phase Commit (2PC) Is Not Preferred?
Traditional distributed transaction protocol:
Two-Phase Commit (2PC)
Problems in microservices:
- Very slow
- Tight coupling
- Blocking transactions
- Not scalable
- Coordinator becomes bottleneck
- Poor cloud-native support
Modern microservices usually avoid 2PC.
Production-Level Distributed Transaction Solutions
- Saga Pattern
- Compensation Transactions
- Outbox Pattern
- Event-Driven Architecture
- Idempotency
- Retry Mechanism
- Dead Letter Queue
Most Popular Solution — Saga Pattern
Saga Pattern is the industry standard approach for handling distributed transactions.
What Is Saga Pattern?
A Saga is a sequence of local transactions.
Each service:
- Executes its local transaction
- Publishes an event
- Next service continues processing
If failure occurs:
- Compensation transactions rollback business operations
Saga Pattern Flow
Step 1:
Order Created
↓
Step 2:
Payment Processed
↓
Step 3:
Inventory Reserved
↓
Step 4:
Notification Sent
Failure Scenario
Order Created
↓
Payment Success
↓
Inventory Failed
↓
Compensation Triggered
↓
Refund Payment
↓
Cancel Order
Final Consistency Achieved
Order Status = CANCELLED Payment Status = REFUNDED Inventory = RELEASED
Two Types of Saga Pattern
- Choreography-Based Saga
- Orchestration-Based Saga
1. Choreography-Based Saga
Services communicate using events.
No central coordinator exists.
Flow
Order Service
↓ publishes event
Payment Service
↓ publishes event
Inventory Service
↓ publishes event
Notification Service
Advantages
- Loosely coupled
- Highly scalable
- Simple for smaller systems
Disadvantages
- Difficult debugging
- Complex event chains
- Hard to track workflow
Kafka Event Example
Order Service
kafkaTemplate.send(
"order-created-topic",
orderId
);
Payment Service Consumer
@KafkaListener(topics = "order-created-topic")
public void processPayment(Long orderId) {
boolean success = paymentGateway.pay();
if(success) {
kafkaTemplate.send(
"payment-success-topic",
orderId
);
} else {
kafkaTemplate.send(
"payment-failed-topic",
orderId
);
}
}
2. Orchestration-Based Saga
A central orchestrator controls the workflow.
Flow
Saga Orchestrator
↓
Calls Order Service
↓
Calls Payment Service
↓
Calls Inventory Service
↓
Calls Notification Service
If Failure Occurs
Inventory Failed
↓
Orchestrator Calls:
Refund Payment
Cancel Order
Advantages
- Easy monitoring
- Centralized workflow
- Easy debugging
- Better for complex business flows
Disadvantages
- Orchestrator becomes central dependency
- Slightly tighter coupling
Production Example Using Orchestrator
@Service
public class OrderSagaOrchestrator {
public void execute() {
createOrder();
processPayment();
reserveInventory();
sendNotification();
}
}
Compensation Transactions
Compensation transaction means undoing completed business actions.
Example
Step 1:
Payment Success
↓
Step 2:
Inventory Failed
↓
Compensation:
Refund Payment
Refund Example
public void refundPayment(Long orderId) {
paymentGateway.refund(orderId);
}
Outbox Pattern
Outbox Pattern ensures reliable event publishing.
Problem Without Outbox
Order Saved in DB
↓
Kafka Publish Failed
Now other services never receive the event.
Outbox Solution
Save business data and event in same database transaction.
Outbox Table
CREATE TABLE outbox_events (
id BIGINT PRIMARY KEY,
event_type VARCHAR(100),
payload TEXT,
status VARCHAR(20)
);
Transactional Save
@Transactional
public void createOrder() {
orderRepository.save(order);
outboxRepository.save(event);
}
Outbox Publisher
@Scheduled(fixedDelay = 5000)
public void publishEvents() {
List<OutboxEvent> events =
repository.findPending();
for(OutboxEvent event : events) {
kafkaTemplate.send(
event.getTopic(),
event.getPayload()
);
event.setStatus("COMPLETED");
}
}
Idempotency
Kafka or APIs may retry messages multiple times.
Without idempotency:
Payment Deducted Twice
Very dangerous in banking systems.
Production Solution
if(transactionAlreadyProcessed(transactionId)) {
return;
}
Retry Mechanism
Temporary failures should be retried automatically.
Spring Kafka Retry Example
@RetryableTopic(
attempts = "3",
backoff = @Backoff(delay = 2000)
)
@KafkaListener(topics = "payment-topic")
public void consume(String message) {
process(message);
}
Dead Letter Queue (DLQ)
If retries fail:
- Message goes to DLQ
- Manual investigation happens
DLQ Example
payment-topic.DLT
Real Banking Example
Suppose:
- Amount debited from sender account
- Receiver account credit failed
Compensation transaction:
Refund sender account
This restores consistency.
Distributed Transaction Challenges
- Network failures
- Duplicate messages
- Partial failures
- Event ordering issues
- Retry storms
- Data inconsistency
Production Best Practices
| Technique | Purpose |
|---|---|
| Saga Pattern | Distributed transaction management |
| Compensation Transaction | Rollback business actions |
| Outbox Pattern | Reliable event publishing |
| Kafka | Async communication |
| Retry Mechanism | Handle temporary failures |
| DLQ | Handle failed messages |
| Idempotency | Avoid duplicate processing |
| PENDING Status | Avoid premature success |
| Distributed Tracing | Track transaction flow |
Final Interview Answer
In microservices architecture, I handle distributed transactions using Saga Pattern instead of traditional two-phase commit because 2PC is slow and not scalable. In Saga Pattern, each microservice performs its local transaction and publishes events through Kafka or RabbitMQ. If any service fails, compensation transactions rollback previously completed business operations. To ensure reliable event publishing, I use the Outbox Pattern. I also implement idempotency, retries, and dead letter queues to safely handle duplicate messages and temporary failures in production systems.