System Design
Object-Oriented Design
Movie Ticket Booking

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:

  1. Optimistic Locking: Use a version field in the database. If the version changes between reading and writing, the booking fails.
  2. Pessimistic Locking: Lock the seat record in the database as soon as the user selects it (usually with a 5-10 minute TTL).
  3. 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.