Designing Data-Intensive Applications
Case 4

End-to-End Booking Flow

Search, hold, pay, confirm — orchestrated with sagas and clear compensation if payment fails.

Diagram

Step-by-step walkthrough

Read path — search and browse

  • ① Browse / filter — Guest runs date, price, and location filters against the Search API.
  • ② Query index — Search API queries Elasticsearch inverted index — never scans inventory rows.
  • ③ Cache hit — Hot listing cards served from Redis when the same queries repeat.
  • ④ GET listing detail — Guest opens a listing page; photos and description from search index or cache.

Write path — hold, pay, confirm

  • ⑤ POST /hold — Guest reserves a slot; Booking API starts a short transaction.
  • ⑥ Lock slot — Inventory row locked in PostgreSQL (SELECT FOR UPDATE or serializable isolation).
  • ⑦ POST /confirm — Guest submits payment with Idempotency-Key to prevent double charge on retry.
  • ⑧ Charge card — Booking API calls Stripe; payment success required before confirm.
  • ⑨ Confirm → COMMIT — Hold converted to sale in one atomic transaction.
  • ⑩ booking.confirmed — Event published to Kafka after commit (outbox pattern).
  • ⑪ Async index update — Consumer updates Elasticsearch availability — derived data, not dual-write.
Diagram
typescript — Booking saga with compensation
// Hold → pay → confirm with compensation
async function bookingSaga(bookingId: string) {
  const hold = await inventory.hold(bookingId);
  try {
    const charge = await stripe.charge({ idempotencyKey: bookingId });
    await inventory.confirm(hold.id);
    await kafka.publish("booking.confirmed", { bookingId, chargeId: charge.id });
  } catch (err) {
    await inventory.release(hold.id); // compensating action
    throw err;
  }
}
In practice

Expedia and Booking.com pipelines mirror this: Elasticsearch for discovery, PostgreSQL for reservations, Stripe/Adyen for payments, Temporal or custom sagas for multi-step checkout.

Key Takeaways
  • API gateway routes search to read services, booking to write service.
  • Hold service writes to inventory DB; payment service calls Stripe.
  • On payment success: confirm hold; on failure: release hold (compensating action).
  • Kafka events update search index and send confirmation emails async.
  • Read replicas serve 'my trips'; primary handles commits.
  • Admin overrides need audit log separate from user-facing booking row.
sagaStripeElasticsearchPostgreSQLcompensationKafkaAPI gatewayQPS