advanced 2 min answer

An order API calls six services synchronously and takes 3 seconds at p95, failing whenever any dependency is degraded. Redesign it.

asyncavailabilityeventsoutbox
Show the full answer Hide the answer

Diagnose with arithmetic

Six synchronous dependencies at 99.9% each give 99.4% — roughly four hours a month, from components that are individually fine. And latency is additive: six sequential calls means the sum of six latencies plus overhead.

Both problems have the same cause, and neither is fixed by making the dependencies faster or more reliable.

Classify each call by one question

Does the caller need the result to continue?

For a typical order: inventory check — yes, it determines whether the order can be accepted. Payment authorisation — yes. Fraud scoring — possibly, depending on whether the business accepts asynchronous review. Shipping estimate — needed for display, but a cached or estimated value is acceptable. Loyalty points — no. Confirmation email — no. Analytics — no.

Three of seven are genuinely required. The rest are synchronous because request-reply is the default in every framework, not because anyone decided.

The redesign

Keep synchronous what determines acceptance, and run those calls in parallel rather than in sequence — latency becomes the maximum rather than the sum, which alone may resolve the p95 problem.

Publish an OrderPlaced event for everything else. Consumers handle loyalty, email, analytics and downstream fulfilment. They can be down; the work completes when they recover; and new consumers are added without touching the order service.

Use cached or default values where a live value is nice to have — a shipping estimate from a cached rate table with a fallback.

The mechanics that make it safe

Outbox pattern for publishing: the order and the event are written in one local transaction. Two independent writes is a dual write, and any failure between them leaves an inconsistency nothing detects.

Idempotent consumers on event ID, because delivery is at-least-once.

Event-carried state — include what common consumers need, so they do not all call back and recreate the coupling you removed.

Timeouts, retries with jitter, and circuit breakers on the calls that remain synchronous.

Name the costs honestly

Eventual consistency: loyalty points appear seconds later. Confirm with the business that this is acceptable — usually it obviously is, occasionally it is not.

Harder debugging, since causation is separated in time. This is what correlation IDs propagated into message headers exist for.

Failure after the caller has gone needs a reporting path: a dead-letter queue with alerting, and a way for support to see that an order's email never sent.

Expected result

p95 drops to roughly the slowest of three parallel calls. Availability improves from 99.4% toward the 99.9% of the remaining dependencies. And the order service stops failing because the analytics pipeline is degraded.

What a strong answer adds

Recognising this as the highest-leverage availability change available in most estates: converting a call to an event removes a dependency from the series entirely, rather than making it more reliable. Reliability work often reaches for redundancy first when the dependency should not have been on the request path at all.