Movie Ticket Booking

Theater Seat Reservation System

Design a movie ticket booking system that holds seats for a few minutes while the user pays, never sells a seat twice, releases abandoned holds, and refunds cancellations by policy.

Overview

A booking system shows the seat map for a show, lets a user pick seats, holds them while the user pays, and turns the hold into a booking when the payment goes through. A show is one screening of one movie on one screen at one time, and each show has its own copy of the seat map.

The hard part is not the catalog of movies and theaters; it is the few minutes between picking seats and paying. Many users open the same popular show at once. Two of them must never both get seat F7, a user who closes the tab must not keep seats forever, and a payment that fails, or succeeds too late, must never leave a seat stuck or a card charged for nothing.

A strong answer says this early, spends most of the hour on the hold, and names the three failure cases: two users race for a seat, a hold expires, a payment fails or arrives late.

Scoping the problem

  • Do we hold seats while the user pays? — Decides whether a seat has a middle state — Yes, for a few minutes
  • How long is a hold? — The timeout on the middle state — A setting; 8 minutes in the demo
  • Can a user book some of the seats they picked? — All or nothing, or partial — All or nothing
  • Does the price depend on the seat? — Price per category per show — Standard and premium
  • What is the cancellation rule? — The refund formula — An example policy: full refund a day ahead, half up to two hours before, none after
  • Is browsing movies and theaters in scope? — A catalog, mostly reads — Mention it, then focus on booking
  • One server or many? — In-memory lock or a database lock — Design in memory, then say how the database does it

Core entities

Seat and ShowSeat are different things, and interviewers check that you see it. A Seat is part of the building and never changes. A ShowSeat is that seat for one show: its price, and whether it is free, held or sold. Putting status on Seat would make seat F7 sold for every show at once.

A seat's life

Expired holds are freed lazily: whenever a request looks at a held seat, it checks held_until against the clock first. No background timer is needed for correctness. A periodic sweep can still run so the seat map shows the seat as free sooner.

Booking flow

Payment calls a slow outside service, so the code does not hold the show lock while it waits. That leaves a gap, and the flow is built around it.

The demo in the implementation walks these cases with a fake clock and a fake payment gateway.

  • 1 — u1 holds A1, A2 — Hold 1 — Both free
  • 2 — u2 holds A2, A3 — refused; A3 stays free — All or nothing
  • 3 — u1 pays for hold 1, twice — one booking, one charge — The retry uses the same idempotency key
  • 4 — u3 holds B1; card declined — B1 free again — Released at once, not after 8 minutes
  • 5 — u4 holds B2; 9 minutes pass; u5 holds B2 — u5 gets it — u4's hold expired
  • 6 — u4 pays — refused, not charged — The hold is checked before charging
  • 7 — u6 holds B3; payment takes 10 minutes; u7 takes B3 meanwhile — u6 refunded — Compensation for a late payment

What you will be able to do

  • Separate the seat in the building from the seat for one show.
  • Draw the seat state machine and explain lazy expiry of holds.
  • Make a multi-seat hold all or nothing under a per-show lock, and prove it with a thread race.
  • Keep the lock out of the payment call and compensate when a late payment loses its seats.
  • Make payment retries safe with an idempotency key, and cancel by a pluggable refund policy.

Functional requirements

  • Shows: a movie on a screen at a start time, with a price per seat category
  • hold(show, seats, user): hold every requested seat or none, for a limited time
  • Expired holds free their seats; a declined payment frees them at once
  • pay_and_confirm(hold): charge and turn the hold into a booking; a retry never charges twice
  • cancel(booking): free the seats and refund by a cancellation policy based on time to showtime
  • available(show): the seats a user can pick right now

Non-functional requirements

  • A seat is never held or booked by two users (a lock per show, all-or-nothing holds)
  • No lock is held during the payment call; shows do not block each other
  • A failed or late payment never leaves a seat stuck or a card charged without a booking
  • Refund policy and payment provider are pluggable interfaces
  • Time is injected, so expiry and refunds are exact in tests

Interview questions

  • How does the database make sure two servers never hold the same seat?
  • Payment failed, or succeeded after the hold expired. What happens?
  • Why lock per show, and why not hold the lock during payment?
  • A blockbuster opens and 50,000 people want the same show. What changes?
  • How would you add dynamic pricing or a waitlist?

Sign up free to read the full topic

More Low-Level Design topics

Start free on Cariara · Coding problems · System design studio