Ticket Booking Rules, and the Question That Changes Everything
The concrete requirements that define BookMyShow's core booking use case, plus the data-ownership decision that forces this design to solve concurrency itself.
The seat layout problem is still ahead, but physical structure is officially settled. What's left, before the harder engineering problems, is the actual use case this whole system exists for: booking a ticket.
The Booking Requirements, Laid Out Plainly
Every one of these came from asking specific, narrow questions about the booking flow rather than trying to guess the whole use case in one go, grouped by what kind of rule each one is:
Must-Have Rules
- Every booking is an advance booking.
- A booking is scoped to exactly one show. Unlike a shopping cart that can span multiple sellers, you can't mix seats from two different theatres or two different showtimes into a single transaction.
- Booking closes 30 minutes before a show starts.
- Seats have to be held during an in-progress transaction.
- Payment is online-only, handled entirely by a third party. The system itself stores nothing more than a transaction ID; every other payment detail lives on the payment provider's side.
- A cancelled show triggers a refund.
- A user can cancel their own booking for a full refund, but cancellation is all-or-nothing. There's no cancelling three seats out of a five-seat booking and keeping the rest.
Do's and Don'ts Within a Booking
- A single booking can mix seat types. Two Gold seats and one Platinum seat in the same booking is fine.
- Selected seats don't have to be consecutive.
- No seat upgrades after a booking is made.
Not Supported, For Now
- No snacks, food, or add-ons.
- No discounts or coupon codes.
The seat-holding rule above is worth sitting with for a second, because it's the first hint of the concurrency problem this whole case study eventually has to solve: two people reaching for the same seat at the same time.
Two Quick Judgment Calls Worth Making Fast
Two smaller questions round out this list, and both are worth resolving in one sentence rather than dwelling on.
Does a QR code or invoice affect your backend design? No, both are just formatted views of data you already have.
Is a feature like search or a wallet worth mentioning even though it's not core? Yes, naming a secondary feature and correctly not building it is good triage, not a wrong answer.
The Question That Changes the Whole Design
Here's the one requirement easy to skip past, and it's bigger than everything above combined: does this system actually own the data it's showing?
For the real BookMyShow, the honest answer is no. It works closer to how MakeMyTrip works for flights: a mediator, calling into each theatre chain's own systems behind the scenes rather than owning seat data itself.
That's also why a theatre without its own booking API historically didn't show up on the platform at all, though the real product has since started partnering directly with smaller theatres too.
This is worth naming as a scoping question at the start of a real interview: "By movie booking platform, do you want an aggregator of other theatres' systems, or a real platform where I own the data myself?" It's the same scoping instinct as the very first business-objective question, just aimed at a bigger axis: not just what the system does, but who actually holds the source of truth.
Why This Design Chooses to Own the Data Anyway
For this case study, the answer is deliberate: build the platform, not the aggregator, specifically to learn how to handle seat locking and concurrency.
If BookMyShow just called into PVR's or INOX's API to reserve a seat, preventing double-booking would be their problem, not this design's. Owning the seat data means this design has to solve that concurrency problem itself, which is exactly the point of the exercise.
There's a useful pattern-recognition nugget hiding in the aggregator version of this design, even though it's not the path being taken. Picture three theatre chains, each with a completely different booking API: PVR expects a reserveSeat(showId, seatCode) call, INOX wants a single giant JSON payload, and a smaller chain only exposes an old-fashioned SOAP endpoint.
A system that has to talk to several different third-party APIs with different interfaces is a textbook case for the Adapter design pattern. One adapter class per chain translates each one's quirks into the exact same reserveSeat() method your own code calls, so the rest of the system never has to know which theatre chain it's actually talking to. Worth remembering for whichever version an actual interview asks for.
Two Settled Details Before the Hard Part
Fairness in a seat-booking race doesn't matter. If two people reach for the same seat at once, neither of them knows the other exists, so there's no requirement that the "right" person wins, only that exactly one of them does.
A show becomes bookable the moment it's created, with no separate waiting period layered on top of the 30-minute pre-show cutoff already covered.
Problem One: Every Theatre's Seats Look Different
Here's the first real engineering problem this design has to solve. Anyone who has booked a movie has actually seen it: the seating chart BookMyShow shows you matches the real, physical shape of that exact auditorium.
One theatre might show a wide, uneven layout with a gap in the middle. Another might be a clean rectangle.
So here's the real question. What does the backend need to store? What does it need to hand over to the frontend, so the frontend can draw the correct shape, for any auditorium, without hardcoding a layout per theatre?
Two tempting answers don't hold up.
The first is storing an uploaded image of the seating chart, a PNG or an SVG. A picture can show any shape, so this feels like it should work.
But a picture is just pixels, not data. There's no way to know which seat in that picture is already booked. There's no way to make one seat clickable either, since a picture has no idea where one seat ends and the next one begins.
The second is just naming every seat, like 1A, 1B, 2A. A name tells you which seat it is. It tells you nothing about where that seat actually sits.
So it can't show a gap, a curve, or a row that's shorter than the one above it.
Neither one gives the frontend what it actually needs. The real answer turns out to be something much simpler than either of them.
Keep reading