Marketplaces, an interactive architecture walkthrough

Ticket booking with seat reservation: how the architecture works

How a ticketing system sells each seat once under heavy demand: waiting room, cached seat map, time-limited holds, conditional updates and safe payment.

This is a learning explainer based on public engineering writing and general design practice. It is simplified, it is not the actual architecture of any company, and it may be inaccurate.

19 components, 19 connections, 13 steps. Updated 27 October 2026.

Interactive diagram

Overview

Selling numbered seats is a contention problem: thousands of buyers want the same few rows at the same moment, and two people must never be sold the same seat. The usual shape is a waiting room that meters entry, a cache that serves the read-heavy seat map, and a single authoritative inventory where a seat moves from available to held to sold through conditional, time-limited updates. A hold gives the buyer a few minutes to pay without locking the seat through a slow card payment, and expired holds return to sale.

Trade-offs and what changes at scale

The hot spot is a handful of rows in one event, so inventory is partitioned by event and the write path is kept short; a very large on-sale is protected by admission control rather than by adding capacity everywhere. Short holds free seats quickly but can expire while someone is paying, and long holds let abandoned carts block inventory. A stale seat map causes failed clicks, not double sales, and the user experience of a lost race needs design. General-admission tickets use a counter instead of individual seats and avoid most of this contention. Queue fairness (first-come or randomised admission) and bot resistance are product decisions with no perfect answer. Refunds for late payments and idempotent payment calls cover the failure cases. This is a simplified teaching model based on general design practice and public documentation, not any ticketing company's actual architecture.

How it works, step by step

The walkthrough above, as text. Each step lights up the components named under it.

  1. A popular on-sale draws far more buyers than the backend can serve at once. The Waiting Room holds the surge outside, shows a queue position, remembers each visitor's place and lets them in at a rate the booking system can handle.

    Components Waiting RoomVia HTTPS

  2. Admitted buyers carry a signed admission token. The gateway checks it, authenticates the user, rate-limits per account and applies bot checks, because scalpers are the hardest traffic in this system.

    Components API GatewayVia admit

  3. Browsing the venue is read-heavy, so the Seat Map Service answers from the Seat Map Cache, never the authoritative inventory. The map may be a few seconds out of date. That is acceptable because the map only guides the buyer, and the real decision is made when a seat is held.

    Components Seat Map Service, Seat Map CacheVia view seats

  4. The buyer picks seats and asks to hold them. The request carries a request ID so a retry after a timeout is not treated as a second hold, and the number of seats per hold is capped.

    Components Reservation ServiceVia hold seats

  5. The hold is one conditional update: mark the seats held by this buyer, with an expiry time, only where they are still available (or their earlier hold has run out), all in one transaction. The database orders competing updates: under PostgreSQL's default isolation, a second update to the same row waits for the first and then re-checks the condition, so one buyer wins and the other sees fewer rows changed and is told the seat is gone. Seat IDs are updated in a fixed order to avoid deadlocks between buyers who want overlapping seats.

    Components Inventory DBVia conditional update

  6. After the hold commits, a seat-changed event is published. Writing the event in the same transaction and relaying it afterwards (an outbox) avoids the case where the seat is held but the event is lost.

    Components Seat EventsVia publish change

  7. The Cache Updater consumes the events and updates the Seat Map Cache, so other buyers see the seats disappear within seconds. A missed update is corrected by the next change or a periodic refresh, and never causes a double sale, because the authoritative inventory decides.

    Components Cache Updater, Seat Map CacheVia consume

  8. Within the hold window (a few minutes, shown as a countdown) the buyer checks out. The Order Service first confirms with the Reservation Service that the hold is still valid and belongs to this buyer, then records an order in the pending state.

    Components Order Service, Reservation Service, Order DBVia checkout

  9. The Order Service charges the card through the Payment Provider, sending an idempotency key so a retried request does not charge twice. Payment is slow and can fail, which is why the seats were held first rather than locked for the whole of it.

    Components Payment ProviderVia charge

  10. The provider's webhook confirms payment. The Order Service marks the order paid and asks the Reservation Service to turn the hold into a sale. If the hold had expired and the seats went to someone else, the payment is refunded rather than oversold. The order is not confirmed to the buyer until this step succeeds.

    Components Order Service, Reservation Service, Inventory DBVia webhook

  11. Abandoned holds are released by the Hold Expiry Worker, which scans for expired ones, so those seats return to sale. The hold condition also treats an expired hold as available, so a late worker does not leave a seat stuck.

    Components Hold Expiry Worker, Inventory DBVia release

  12. Some designs guard seats with a lock in Redis. A lock with an expiry can lapse while a paused client still believes it holds it, so two buyers may both proceed (Martin Kleppmann's analysis of distributed locks). Keeping the guarantee in a conditional write at the database, with locks only as a speed-up, avoids relying on timing.

    Components Reservation Service

  13. Observability tracks hold conflicts, queue length and admission rate, expiry backlog, and a periodic check that no seat is sold twice. Oversell checks matter even when the design should prevent it.

    Components Observability, Waiting Room, Order ServiceVia metrics

Sequence diagram

The same walkthrough as a UML sequence diagram, one lane per layer or group (14 components in all), left to right in the order a request reaches them, numbered by step. A loop on a lane is a step that stays inside that group. Solid arrows are synchronous calls, dashed are asynchronous.

Ticket booking with seat reservation: sequence diagram of the walkthroughClientsEdgeSeat Map (ReadPath)Reservation(Write Path)Orders andPaymentObservabilityHTTPS1admit2view seats3read3hold seats4conditional update5publish change6consume7update7checkout8verify, confirm8create order8charge9webhook10verify, confirm10conditional update10release11scan expired11handles request12metrics13queue length13metrics13

Key components

Edgelayer

  • API Gateway: admission token check, auth, rate limiting, bot checks
  • Waiting Room: admits buyers at a rate the backend can take

Seat Map (Read Path)domain

  • Cache Updater: applies seat changes to the cache
  • Seat Map Cache: availability snapshot, a few seconds stale (redis)
  • Seat Map Service: seat availability for the venue view

Reservation (Write Path)domain

  • Hold Expiry Worker: releases holds that ran out
  • Inventory DB: each seat: state, holder, hold expiry; partitioned by event (postgresql)
  • Reservation Service: hold, release and confirm seats
  • Seat Events: seat state changes (kafka)

Orders and Paymentdomain

  • Order DB: orders and their payment state (postgresql)
  • Order Service: creates orders, drives payment, confirms seats
  • Payment Provider: charges the card, sends webhooks

Observability

  • Observability: hold conflicts, queue length, oversell checks

Clientslayer

  • Booking Client: seat map, hold countdown, checkout

Sources

Public writing this explainer draws on. Read these for what the real systems actually do.

Make it yours

Copy this diagram and its walkthrough into your own workspace, then change anything: swap Postgres for DynamoDB, add a cache, rewrite the narration. Free, and it uses no AI credits.

This is a learning explainer based on public engineering writing and general design practice. It is simplified, it is not the actual architecture of any company, and it may be inaccurate. Real systems differ in the details. Browse the library.