What's Left for You to Implement
Two pieces of the book-ticket flow that were designed in full but never actually coded: taking the real database lock, and calculating the amount a user actually pays.
Every design decision behind booking a ticket has been made. Two pieces of the actual implementation were designed in full but never written, and they're worth building yourself rather than left unfinished.
Exercise One: Take the Real Database Lock
The reasoning is already fully worked out. A brief database lock has to guard the moment bookTicket checks whether every requested seat is still available, for exactly as long as that check and the following status update take, nothing longer. SERIALIZABLE isolation — the strictest level a database offers, guaranteeing that no other transaction can read or touch the same rows until this one finishes — is the textbook mechanism for it, and holding it for the length of an entire payment flow would be a disaster at scale.
What's missing is the actual wiring: taking that lock for real around the specific lines that check and then update seat status, and confirming it actually works. If you're not sure where to start, this is the shape of the work:
- Look up how your ORM or database driver lets you set the transaction isolation level for a single block of code (search terms: "pessimistic locking" or "
SELECT ... FOR UPDATE" for most SQL databases, or your ORM's own isolation-level annotation). - Wrap just the seat-availability check and the status update in that transaction — nothing before or after it.
- Prove it works: write a small script or test that fires two requests for the same seat at almost the same instant (two threads, or two terminal windows running
curlback to back), and confirm only one succeeds while the other fails cleanly with the seat-not-available exception already designed for exactly this case.
You don't need to get the locking mechanism exactly right on the first attempt — getting a test that reliably reproduces the race, then watching your fix actually close it, is most of the learning here.
Exercise Two: Calculate the Amount a User Actually Pays
The ticket's amount has stayed a placeholder since it was first created. Summing it up isn't the hard part: every seat's price comes from its ShowSeatType row, and adding those together for every seat in the booking is a straightforward loop.
The real exercise is the part layered on top of that sum: tax. A theatre in one city, a seat of one type, a show at one time, any of these might reasonably need a different tax treatment applied. That's exactly the shape of problem the Strategy pattern exists for — a general, reusable design move, not something specific to tax or to BookMyShow: define one interface for "calculate the tax," then write a separate class behind it for each way tax actually gets calculated, so a new rule means adding a class, not editing existing code.
Start small and concrete:
- Write
calculateAmount()as a plain loop summing each seat's price from itsShowSeatTyperow. Get this right and tested before touching tax at all. - Define a one-method interface, something like
TaxStrategywith acalculate(amount)method. - Write your first real implementation of it — even a flat percentage is enough to start — and call it from
bookTicketthrough the interface, not the concrete class directly.
Once that's in place, adding a second tax rule later means writing a second class, never editing the first one or bookTicket itself. That's the actual payoff worth building this yourself to feel.
Both exercises are small enough to finish in an evening and real enough to teach more than reading ever will. The two dozen decisions made across this series only turn into a skill once you've made a few of the smaller ones yourself — this is where that happens.
Keep reading