Data and Transactions Questions
Why can't you use a normal @Transactional across microservices? Why not 2PC?
@Transactional covers one database connection. Order and payment live in different databases, so there is no single transaction. Two-phase commit (XA) exists but it is blocking (a coordinator failure locks resources), slow, not supported by Kafka/most NoSQL/cloud services, and it couples services' availability. Microservices use sagas with eventual consistency instead.
What is the Saga pattern? Choreography vs orchestration?
A saga is a sequence of local transactions; each step publishes an event or replies, and if a step fails, earlier steps are undone by compensating transactions (refund, release stock, cancel order).
| Choreography | Orchestration | |
|---|---|---|
| Control | each service reacts to events | a central orchestrator sends commands |
| Flow | OrderCreated → StockReserved → PaymentDone | OrderSaga: reserveStock() → charge() → confirm() |
| Pros | simple, no central point, loose coupling | flow visible in one place, easier to change and monitor |
| Cons | hard to see the whole flow, cyclic event dependencies | orchestrator is extra logic to own |
| Use for | 2–4 steps | many steps, complex rules (tools: Temporal, Camunda, Axon) |
What is the transactional outbox pattern?
Problem (dual write): saving the order in MySQL and sending OrderPlaced to Kafka are two systems. If the app crashes between them, you get an order without an event, or an event without an order.
Solution: in the same local transaction, insert the order and a row in an outbox table. A relay (a scheduled poller, or CDC with Debezium reading the binlog) publishes outbox rows to Kafka and marks them sent.
@Transactional
public Order placeOrder(PlaceOrder cmd) {
Order order = orders.save(Order.from(cmd));
outbox.save(new OutboxEvent("Order", order.getId(), "OrderPlaced", json(order)));
return order; // both rows commit or neither
}Note: The relay may publish a row twice (crash after send, before marking), so consumers must be idempotent.
What is idempotency and how do you make an API idempotent?
An operation is idempotent if doing it many times has the same effect as doing it once. GET, PUT and DELETE are idempotent by definition; POST is not. For POST /payments:
- Client sends a unique
Idempotency-Keyheader (UUID generated once per checkout attempt). - Server inserts the key into a table with a unique constraint (or Redis
SET NX) before processing. - If the key exists and is completed, return the stored response; if in progress, return 409.
- Keys expire after e.g. 24 h.
For Kafka consumers, use the event id the same way (a processed_events table in the consumer's DB, written in the same transaction as the business change).
How do you handle eventual consistency in the UI and in reads?
- Model intermediate states explicitly:
PENDING → CONFIRMED / CANCELLED; show "Order placed, confirming payment". - Push the final status to the client (WebSocket / SSE / push notification) or let it poll.
- Read-your-own-writes: return the created resource in the response instead of re-reading a lagging read model.
What is CQRS and when would you use it?
Command Query Responsibility Segregation: separate write model (normalised tables in the owning service) from read model(s) built from events and optimised for queries (Elasticsearch for product search, a denormalised "order history" view joining order + product + payment). Use it when reads are much heavier or shaped very differently from writes. Cost: extra infrastructure and read models that lag slightly.
How do you prevent overselling stock when many users buy the last item?
- Do the check and the decrement atomically in inventory-service:
UPDATE stock SET qty = qty - 1 WHERE product_id = ? AND qty >= 1; 0 rows updated means out of stock. - Or optimistic locking with
@Versionand retry on conflict. - For flash sales: pre-load stock in Redis and use atomic
DECR/ Lua script, then persist asynchronously. - Reserve stock with a TTL during checkout and release it in the saga's compensation if payment fails.