Designing a movie ticket booking system like BookMyShow is one of the most popular LLD interview problems at Swiggy, Razorpay, Paytm, and other product companies. The key challenge is handling concurrent seat booking — preventing two users from booking the same seat simultaneously. This guide walks through the full design.
Core Requirements
- Browse movies, theaters, and showtimes
- View seat layout with real-time availability
- Select and book seats — prevent double booking
- Apply coupons and calculate final price
- Generate booking confirmation and ticket
- Cancellation with refund policy
Core Entities
- Movie — title, genre, duration, language
- Theater — name, location, screens
- Screen — has a seat layout, belongs to a theater
- Show — a specific Movie playing on a Screen at a time
- Seat — row, column, type (Regular/Premium/Recliner)
- ShowSeat — seat state for a specific show (Available/Locked/Booked)
- Booking — user, show, seats, payment, status
- Payment — method, amount, transaction ID
The Concurrency Problem — Most Important Part
Two users selecting the same seat simultaneously is the crux of this problem. The solution is temporary seat locking:
enum ShowSeatStatus { AVAILABLE, LOCKED, BOOKED }
class ShowSeat {
seat: Seat;
status: ShowSeatStatus;
lockedBy: string | null; // userId
lockExpiry: Date | null; // 10 minutes from lock time
}
class BookingService {
lockSeats(showId: string, seatIds: string[], userId: string): boolean {
// In a real system: SELECT FOR UPDATE or optimistic locking
const seats = this.getAvailableSeats(showId, seatIds);
if (seats.length !== seatIds.length) return false; // Some already taken
seats.forEach(s => {
s.status = ShowSeatStatus.LOCKED;
s.lockedBy = userId;
s.lockExpiry = new Date(Date.now() + 10 * 60 * 1000);
});
return true;
}
}Booking State Machine
enum BookingStatus {
INITIATED, // Seats locked, payment pending
CONFIRMED, // Payment successful
CANCELLED, // User cancelled or lock expired
EXPIRED // Lock timed out before payment
}Pricing Strategy
interface PricingStrategy { getPrice(seat: Seat, show: Show): number; }
class WeekendPricing implements PricingStrategy {
getPrice(seat: Seat, show: Show): number {
const base = seat.type === SeatType.PREMIUM ? 400 : 200;
return base * 1.25; // 25% weekend surcharge
}
}
class WeekdayPricing implements PricingStrategy {
getPrice(seat: Seat, show: Show): number {
return seat.type === SeatType.PREMIUM ? 350 : 180;
}
}Cancellation Policy — Template Method
abstract class CancellationPolicy {
cancel(booking: Booking): number { // returns refund amount
if (!this.canCancel(booking)) throw new Error("Cannot cancel");
const refund = this.calculateRefund(booking);
this.releaseSeats(booking);
return refund;
}
abstract canCancel(booking: Booking): boolean;
abstract calculateRefund(booking: Booking): number;
}
class Before24HourPolicy extends CancellationPolicy {
canCancel(b: Booking) { return hoursUntilShow(b) > 24; }
calculateRefund(b: Booking) { return b.totalAmount * 0.8; } // 20% fee
}Common Interview Questions
- "How do you prevent double booking?" → Seat locking with expiry + SELECT FOR UPDATE in DB
- "What if the payment fails after locking?" → Lock expires, seats released back to available
- "How do you add dynamic pricing?" → Inject different PricingStrategy based on day/demand
- "How does the seat map work?" → Screen has a 2D grid of Seats; ShowSeat maps Show+Seat to status
Companies That Ask This
BookMyShow (obviously), Swiggy, Paytm, Razorpay, Cleartrip, MakeMyTrip, and most fintech/consumer companies in India. This is a top-5 most asked LLD problem.