99.5%
checkout success if all five calls must succeed
Each service is up 99.9%: 0.999⁵ ≈ 99.5%. Over a 30-day month that’s about 3.6 hours of failed checkouts instead of 43 minutes.
Publish one fact. Let whoever cares react.
Orders saves the order, appends one OrderPlaced and moves on, one durable write, not five calls. A new ETA model just subscribes.
Event or command?
Bad: A passive-aggressive command
Payments publishes RefundRequired, then breaks if refunds ignores it.
Good: A fact about the past
OrderPlaced can’t be refused; anyone may react. Must one party act? Send a command.
What decoupling costs
- Eventual consistency: show “finding a rider” until
RiderAssigned - At-least-once delivery: consumers dedupe on event ID
- Order only per key: key by order ID
- No call stack: carry a correlation ID
Orders commits the order, then crashes before publishing OrderPlaced. What does the kitchen hear?
The catch
Nothing: the dual-write trap. Insert the event into an outbox table in the same transaction; a relay publishes it.
Order 7781 is stuck. Who knows where?
Choreographed, every service reacts to the last event, so the flow lives in four codebases. Orchestrate the core flow; choreograph side effects like loyalty and analytics.
When not to use events
- Caller needs an answer now: login, UPI authorisation
- Simple CRUD: one database, one team
- Cross-entity invariants: never sell a berth twice
- A team new to asynchronous systems
Remember
Announce facts; let consumers react independently. Publish via an outbox, key by entity, dedupe everywhere, and keep what users wait on synchronous.