System Design Architecture

Problem

Design a ticket booking system for high-demand events (concerts, sports) where the core constraint isn't scale for its own sake — it's guaranteeing a seat is never sold to two different people while a flash-sale crowd hammers the same inventory at once.

Requirements

Functional

  • Browse events/venues, view a seat map, select seats, and complete a purchase.
  • A seat is never double-booked, even under concurrent requests for the same seat.
  • A temporary hold on selected seats while payment completes (a 5–10 minute reservation window).
  • Support flash-sale on-sale moments with a large simultaneous traffic spike.

Non-functional

  • Strong consistency for seat state, even under concurrency — this is a correctness problem, not just a performance one.
  • Stay available during a flash-sale spike rather than falling over at the exact moment it matters most.
  • Low-latency seat-map reads despite writes needing strict consistency.

Architecture

  • Seat inventory service: owns seat state (available / held / booked) per venue/event, backed by a strongly consistent store (row-level locking, or optimistic concurrency via a version/CAS column).
  • Reservation flow: selecting seats acquires a short-lived hold with an expiry, not an immediate booking; an expiry sweeper (or store TTL) releases holds that never convert.
  • Waiting room: a virtual queue in front of the booking flow admits users into seat-selection at a controlled rate, so the inventory service never sees more concurrent writes than it can safely serialize.
  • Read path: a denormalized, eventually-consistent seat-map cache serves browsing users cheaply; the actual hold/booking write always goes through the strongly consistent inventory service, the sole source of truth.
  • Payment: a hold is created first, payment is processed against a provider, and only a successful payment converts the hold into a confirmed booking; a failed or timed-out payment releases it.

Key decisions

  • Strong consistency (locking or optimistic-concurrency retry) on the seat write path, not an eventually-consistent design — double-booking is a correctness bug users notice immediately and it costs money and trust directly.
  • A hold-then-confirm two-phase flow over immediate booking, since payment latency is unpredictable (redirects, 3DS, provider timeouts) and a seat can't be held open-endedly without a release mechanism.
  • Admission control (waiting room) at the edge rather than expecting the strongly-consistent write path to gracefully absorb an unbounded spike — easier to control demand than to make a consistent store elastically scale to it.

Tradeoffs

  • The eventually-consistent seat-map cache means a user occasionally sees a seat as available that's gone by the time they click — an accepted UX tradeoff over paying for a strongly-consistent read on every page view.
  • The waiting room adds complexity and can frustrate queued users during a real spike, but the alternative is the inventory service falling over entirely at the moment it matters most.
  • Hold-expiry windows: too short and legitimate slow payers lose their seat; too long and inventory sits needlessly locked away from other buyers during a high-demand sale.

Failure modes

  • Inventory service partition/failure: the system should fail closed (refuse new holds) rather than risk double-booking by degrading to looser consistency.
  • Payment timeout after a hold is created needs an async reconciliation job that checks payment status and confirms or releases the hold, rather than trusting only the synchronous request path.
  • A waiting-room admission bug (admits too fast) still needs the inventory service's per-seat locking as the final correctness guardrail — defense-in-depth here is cheap relative to a double-booking incident.

Capacity estimates

  • A flash sale for a major venue (say 20K seats) can see hundreds of thousands of concurrent hopeful buyers in the opening seconds — the admission-control layer is sized to that demand/supply mismatch, not the inventory service.
  • Seat-inventory writes are bounded by the number of seats, not the number of requesting users — a 20K-seat venue is at most 20K hold/booking transactions however many people are trying to get them.
  • The seat-map read path has to absorb the full request volume (hundreds of thousands of reads/sec at peak), which is exactly why it's served from cache rather than the consistent store.

What I'd change at 10x scale

  • Push admission control further out to the edge/CDN (token-bucket admission at an edge function) rather than the origin, so origin infrastructure never absorbs the full brunt of a spike before shedding load.
  • Explicit per-venue sharding of the inventory service, so one venue's flash sale can't contend with unrelated venues' normal traffic on shared infrastructure.
  • Predictive pre-scaling ahead of a known on-sale moment, rather than purely reactive autoscaling — flash-sale traffic is a scheduled event, not an unpredictable one.