This content originally appeared on DEV Community and was authored by Mohamed Hassan
This is the first article in a series that walks through a simple order solution for a hypothetical company called “Simply Order”. The company expects high traffic and needs to design a resilient, scalable, and distributed Order system.
We’ll go step by step, building the system and discussing the challenges along the way, while exploring the trade-offs between possible solutions.
In this first article, we’ll describe the problem and review different approaches — with their pros and cons — before choosing our recommended path. In the upcoming articles, we’ll implement the solution, share code examples, and highlight best practices.
The Problem
The company chose a microservice architecture to build their system. They started with an Order microservice, but soon realized it was becoming a bottleneck.
This service has to handle multiple responsibilities:
Accept orders and store them.
- Communicate with the Inventory Service to check product availability.
- Communicate with the Payment Service to authenticate and process user payments.
- Communicate with the Fulfillment Service to start shipping and logistics.
- Finally, notify users and other interested participants through different channels — e.g., messages, emails, and so on.
These multiple operations should be consistent and ideally treated as a single unit of work. But here’s the problem:
- Each microservice has its own DB → can’t use a single DB transaction across them.
- One service may commit successfully, while others fail. This means one service may commit successfully, while others fail.
- Some services may even use NoSQL databases, which don’t support traditional transactions at all.
- And to make it worse, some microservices might be unreachable at the time of the operation.
- So the big question is: how do we manage these operations and keep the system consistent in the chaotic world of microservices?
Solutions
Two-Phase Commit (2PC)
A straightforward solution that often comes to mind is Two-Phase Commit (2PC).
Some relational databases and drivers support it, making it possible to coordinate a distributed transaction.
Here’s how it works:
- A coordinator talks to the participants (other microservices or their databases) and asks them to prepare their transaction.
- If all participants reply with yes (prepared), the coordinator sends a commit message to everyone.
- If one or more reply with no (or fail to respond), the coordinator sends a rollback message to all participants.
Pros & Cons
Pros
- Guarantees atomicity across multiple services.
- Conceptually simple — feels like a “distributed version” of a database transaction.
Cons
- Blocking: the prepare phase locks database resources until the final commit/rollback, which hurts scalability. Also, if any participant is down or unresponsive, the whole transaction may block and hold locks.
- Single point of failure: if the coordinator crashes, participants may stay locked indefinitely.
- Limited Support: Not all databases or drivers support XA/2PC transactions (especially many NoSQL or cloud-native DBs).
Because of these limitations, 2PC is generally not suitable for microservices in a cloud-native environment.
Saga
Saga splits one big transaction that involves multiple services into a sequence of local transactions owned by each service. If one service fails to commit its transaction, other services get informed about that failure, and instead of rollback (services do not block, they already committed their changes), they perform a compensating transaction. i.e. if the Inventory service deducted/reserved certain items, it will create another transaction to add/free the deducted items.
But how can each service be informed about other services?
There are 2 types of Saga: Choreography and Orchestration
There are 2 types of Saga: Choreography and Orchestration
Choreography (event-driven)
Services emit domain events, i.e. OrderCreated, InventoryReserved, PaymentFailed, etc. Other interested services subscribe to these events and can create a compensating transaction if needed. i.e. when a PaymentFailed event is published, the Inventory service creates a compensating transaction to free reserved items, the Order service updates its order status to failed, and so on.
Pros
- Natural for event-driven systems.
- Simple to start and each service is decoupled.
Cons
- Harder to manage complex flows, timeouts, and branching.
- Risk of spaghetti events: imagine if just 5 services are involved, and each service could listen to the others!
Orchestration (command-driven)
A central orchestrator sends commands to other services and waits/listens for results in a non-blocking manner.
The workflow is explicit code from domain logic, centralized to manage retries, timeouts, compensations, etc.
Pros
- One source of truth, i.e. single place for flow, retries, and timeouts.
- Clear visibility even with complex processes.
Cons
- The orchestrator could be a single point of failure, so it is a critical component.
- Slightly tighter coupling with the orchestrator API, as typically it is a 3rd-party library/component.
Saga in both modes shares some common pros and cons.
Pros
- Saga fits microservices naturally as each microservice is loosely coupled from the others.
- Saga is non-blocking by the nature of its communication, which is critical for the scalability of microservices.
Cons
- Eventual consistency: the system is temporarily inconsistent until all steps (or compensations) finish.
- Complex compensation logic: “undoing” business actions is not always straightforward.
- Increased complexity: i.e. handling idempotency and compensating logic.
Wrapping up
In this article, we looked at the problem of distributed transactions, why 2PC is not suitable for microservices, and how the Saga pattern (choreography and orchestration) helps solve it.
In the next article, we’ll start implementing the solution using Temporal as an orchestrator-based Saga. We’ll walk through code examples and best practices step by step.
👉 Follow this series if you’re interested in building resilient microservices, and feel free to share your thoughts or questions in the comments — I’d love to hear your perspective.
This content originally appeared on DEV Community and was authored by Mohamed Hassan

Mohamed Hassan | Sciencx (2025-08-27T18:40:25+00:00) Distributed Transactions in Microservices: Why 2PC Doesn’t Fit and How Sagas Help. Retrieved from https://www.scien.cx/2025/08/27/distributed-transactions-in-microservices-why-2pc-doesnt-fit-and-how-sagas-help-3/
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.