Design Example: Movie Ticket Booking System
Designing a Movie Ticket Booking System (like BookMyShow or Fandango) is a common OOD interview question that tests your ability to handle complex relationships and concurrency.
Step 1: Requirements Gathering
Core Use Cases:
- Search for movies by title, genre, or city.
- Display theaters showing a specific movie.
- Select a theater, a showtime, and available seats.
- Handle different seat types (Silver, Gold, Platinum).
- Process bookings and payments.
- Send notifications (Email/SMS) after booking.
Concurrency Requirements:
- Two users should not be able to book the same seat simultaneously.
Step 2: Identify Core Objects
- Movie: Properties like title, duration, genre, language.
- Cinema / Theater: Contains multiple Rooms (Screens).
- CinemaRoom: Holds the seating layout.
- Show: Links a Movie, a Room, and a Showtime.
- Seat: Represents an individual seat in a Room.
- Booking: Represents a user's reservation.
- Payment: Handles transactions.
Step 3: Design Class Diagram
Deep Dive: Handling Concurrency
One of the most critical parts of this design is finding a way to handle Race Conditions when two users try to book the same seat.
Solutions:
- Optimistic Locking: Use a version field in the database. If the version changes between reading and writing, the booking fails.
- Pessimistic Locking: Lock the seat record in the database as soon as the user selects it (usually with a 5-10 minute TTL).
- Distributed Locks: Use Redis or a similar tool to manage locks across multiple app servers.
Step 4: Implementation in TypeScript
enum BookingStatus {
PENDING,
CONFIRMED,
CANCELLED
}
class Seat {
private isReserved: boolean = false;
constructor(public id: string, public price: number) {}
public reserve(): boolean {
if (this.isReserved) return false;
this.isReserved = true;
return true;
}
}
class Booking {
private status: BookingStatus = BookingStatus.PENDING;
constructor(
public bookingId: string,
public show: Show,
public seats: Seat[]
) {}
public confirmBooking() {
this.status = BookingStatus.CONFIRMED;
}
}
class BookingManager {
private static instance: BookingManager;
private bookings: Map<string, Booking> = new Map();
static getInstance() {
if (!this.instance) this.instance = new BookingManager();
return this.instance;
}
public async createBooking(show: Show, seats: Seat[]): Promise<Booking | null> {
// Attempt to reserve all seats (Atomic operation)
const allSeatsReserved = seats.every(seat => seat.reserve());
if (allSeatsReserved) {
const booking = new Booking(Date.now().toString(), show, seats);
this.bookings.set(booking.bookingId, booking);
return booking;
}
return null; // Some seats were already taken
}
}Advanced Topics: Strategy for Pricing
Different theaters might have different pricing rules (e.g., peak hours, discounts).
interface PricingStrategy {
calculate(basePrice: number): number;
}
class PeakHourPricing implements PricingStrategy {
calculate(basePrice: number) { return basePrice * 1.5; }
}
class StudentDiscountPricing implements PricingStrategy {
calculate(basePrice: number) { return basePrice * 0.8; }
}Wrap Up
The Movie Ticket Booking system is a step up from the Parking Lot because of the high volume of concurrent transactions and the complexity of its seat-management logic.
[!IMPORTANT] In a real-world scenario, you would integrate a distributed cache like Redis to manage seat locks and ensure strong consistency across your distributed system.