Distributed transactions and sagas
Replace the transaction you cannot have across services with a sequence of steps and compensations.
Open this lesson in the learning hubKey points
- There is no
@Transactionalacross services. Two-phase commit holds locks over the network: slow, fragile, rarely supported. - A saga is a chain of local transactions. Each step commits inside one service and announces what happened.
- If a step fails, run compensations backwards: refund, release the reservation. You undo with new transactions, never a rollback.
- Choreography: each service reacts to events from the others. No coordinator, but the flow lives nowhere and is hard to follow.
- Orchestration: one component drives each step and stores progress. More moving parts, far easier to debug and to resume.
- Design the compensations first. Some steps cannot be undone, so send the email and ship the box last.
Example
// Orchestrated saga: one place owns the happy path and the undo path.
@Component
class PlaceOrderSaga {
void onOrderPlaced(OrderPlaced event) {
String orderId = event.orderId();
// Persist state after every step so a crash can resume, not restart.
try {
inventory.reserve(orderId, event.sku(), event.qty()); // step 1
payments.charge(orderId, event.amountCents()); // step 2
shipping.schedule(orderId); // step 3
orders.markConfirmed(orderId);
} catch (PaymentDeclined e) {
inventory.release(orderId); // undo step 1
orders.markRejected(orderId, "payment declined");
} catch (ShippingUnavailable e) {
payments.refund(orderId); // undo step 2
inventory.release(orderId); // undo step 1
orders.markRejected(orderId, "no shipping slot");
}
}
}
No global rollback exists, so plan the undo for every step before you write the step.
This is a reading copy. The full lesson — with the visual explainer, the interactive lab and a Run button for the code — lives in the Microservices course, and every lesson in it is listed on the Microservices contents page.