skip to content

A team is integrating a pricing service with three downstream consumers: a checkout UI that needs the current price before rendering, an analytics warehouse that aggregates prices nightly, and a recommendation engine that reacts to price drops. How should the choice between request-reply and event-driven integration differ across these three consumers, and what do you give up by picking event-driven for all three anyway?

level: seniorimportance: must knowfreq 70%

answer

  1. pull vs push
  2. eventual consistency cost
  3. blast radius of sync calls
  4. who's blocked right now?
  5. silent staleness vs loud timeout

basics

~20 s

Pick request-reply when a consumer needs the answer right now to keep working, like a checkout screen. Pick events when a consumer just needs to know 'something changed' and can react later, like analytics or recommendations. Using events everywhere adds delay and complexity where you didn't need it.

solid answer

~50 s

The checkout UI has a hard, immediate dependency — it cannot render without today's price — so request-reply is correct: it pulls exactly the data it needs, when it needs it, with an easy-to-reason-about synchronous failure mode (timeout/fallback). The analytics warehouse and recommendation engine don't need the price right now — they need to know 'a price changed' and can process on their own schedule, so event-driven ('PriceChanged' events) fits: it decouples the pricing service from needing to know who's listening, and adding a fourth consumer later requires zero changes to the pricing service. Forcing the checkout UI onto events instead means either reading a local cache that might be stale (wrong price shown) or building a synchronous wait-for-the-next-event bridge, which just reinvents request-reply with more latency. Forcing analytics onto request-reply means the pricing service now must know about and call every downstream consumer directly, coupling it to consumers it shouldn't need to know exist.

go deeper

for a junior

Can say request-reply is for 'need it now' and events are for 'tell me later,' with one example of each.

for a middle

Picks the right pattern per given scenario and can name eventual consistency as a cost of the event-driven choice.

for a senior

Actively designs a mixed integration (as in the checkout/analytics/recommendation example), articulating the coupling and blast-radius trade-off for each consumer individually.

for a principal

Sets integration-pattern guidelines across a platform (e.g., 'critical-path reads are always request-reply, cross-domain fan-out is always events'), and arbitrates disputes when a consumer team wants real-time guarantees an event-driven design structurally can't provide without redesign.

## The one question that decides it Request-reply and event-driven are two different answers to the question 'how does a consumer find out something from a producer,' and the right choice per interaction hinges on one question: does this consumer need to pull a specific answer right now, or does it just need to know that something happened and can react on its own schedule? Getting this wrong in either direction — forcing a real-time need through an asynchronous channel, or forcing a non-time-sensitive need through a synchronous call — is one of the most common integration-design mistakes in practice. ## Request-reply: the pull model Request-reply means the consumer initiates a call, specifies exactly what it wants, and receives a targeted answer back over the same interaction. It's a 'pull' model: - the consumer controls timing, and the producer only does work when explicitly asked - this fits any interaction where the consumer's own progress is blocked on the answer — a checkout page can't render a price it doesn't have, a payment flow can't proceed without an authorization result - the producer has to know it's being called and expose a stable query interface, but doesn't need to know who its consumers are or track their state ## Event-driven: publishing a fact Event-driven integration flips the direction: the producer, when something relevant happens, publishes a fact about it (`PriceChanged`) without knowing or caring who, if anyone, is listening. Consumers subscribe to facts they care about and react independently, on their own timeline. This is a 'push' model from the producer's side but a 'pull-when-ready' model from each consumer's — nobody is blocked waiting on anybody else in real time. This fits interactions genuinely decoupled in time from the producer's own work: an analytics pipeline aggregating overnight, a recommendation engine updating a model — none of these need to complete before the producer's own transaction is done. ## The trade-off The trade-off is about coupling and blast radius versus latency and consistency. | Pattern | What it does to coupling and freshness | |---|---| | **Request-reply** | Couples the producer's availability to every synchronous consumer's user experience — if the pricing service is slow, the checkout page is slow, full stop, and that's an honest, visible coupling easy to monitor. It also means the producer's read path scales with every synchronous caller's traffic directly. | | **Event-driven integration** | Removes that direct coupling — the pricing service's own transaction latency no longer includes 'wait for analytics' — but introduces eventual consistency: any event-driven consumer is, by construction, looking at slightly stale information between when the event fires and when it's processed, and if that lag matters for correctness, that's a real cost accepted by choosing this pattern, not a bug. | ## Failure modes The failure modes differ concretely. - **A request-reply consumer forced onto a time-sensitive need that breaks** typically fails visibly and immediately — a timeout, an error, a blank field on the checkout page — unpleasant but debuggable. - **A consumer forced onto events when it actually needed real-time data** fails more subtly: it silently displays or acts on stale data with no error at all, because from the event pipeline's perspective nothing is wrong — the event just hasn't arrived yet. This is the more dangerous failure mode because it doesn't page anyone; it quietly produces wrong answers, usually discovered by a user complaint long after the design decision was made. ## The inverse mistake The inverse mistake — forcing a non-time-sensitive consumer onto request-reply — has its own cost: it couples the producer to consumers that never needed real-time coupling. If the pricing service had to synchronously call analytics and recommendations on every price change: - adding a fourth consumer means changing the pricing service's code - an outage in one blocks or delays consumers who never needed real-time freshness - and a slow analytics call can slow down the pricing service's core write path for no functional reason This is the classic case for preferring events for one-to-many, fire-and-forget-style fan-out even when strict ordering or delivery guarantees — the deeper mechanics of the broker itself — are a separate, later design decision. ## A concrete real-world pattern A concrete real-world pattern: e-commerce platforms almost universally use request-reply for the checkout-critical path — inventory check, payment authorization, tax calculation — because the customer is staring at a screen, and use event-driven fan-out (`OrderPlaced`, `InventoryAdjusted`) for everything after the order is durably recorded: fulfillment kickoff, email confirmation, analytics, recommendation-model updates. The dividing line is always the same: is someone actually blocked on this answer right now?

  • Why is a stale-data failure from an event-driven integration more dangerous in production than a timeout from a request-reply integration?
    A timeout is loud — it fails visibly, gets logged, often pages someone, and the consumer knows it doesn't have an answer. A stale-data failure from an event that hasn't arrived yet is silent — the consumer has an answer, just an outdated one, and nothing in the system flags that as wrong, so it's usually caught by a user noticing incorrect behavior rather than by monitoring.
  • If the pricing service had to synchronously notify both the analytics warehouse and the recommendation engine on every price change, what specific new risk does the pricing service take on?
    The pricing service's core write path now depends on two consumers it functionally shouldn't need to know about, so a slow or down analytics/recommendation system can delay or fail the price update itself. It also means every future consumer added requires a code change in the pricing service, coupling its release cadence to consumers that never needed real-time coupling in the first place.
  • Does choosing event-driven integration for the recommendation engine mean you've given up on it ever reflecting a price change quickly?
    No — event-driven doesn't mean slow, it means decoupled in time; a well-tuned consumer can process an event within milliseconds of publication, so freshness depends on consumer processing lag, not the pattern itself. What you've given up is the guarantee that the consumer's view is never stale even for a moment, which request-reply's pull-on-demand model provides by construction.

Request-reply is asking a librarian a question at the desk and waiting for the answer. Event-driven is the librarian posting a notice on a public board when a new book arrives - you check the board on your own schedule, or you don't, and nobody at the desk is blocked either way.

saying these in an interview costs you the question

  • Picks one pattern for the whole system instead of per-interaction
  • Doesn't identify eventual consistency as an explicit cost of choosing events
  • Assumes event-driven consumers always process near-instantly with no lag
  • Can't explain why forcing a time-sensitive read through events is risky
  • Thinks request-reply has no coupling cost as long as there's a timeout
  • Conflates 'event-driven' with a specific broker technology's internal reliability mechanics

context