Concurrency / Transactional• Patterns: Observer, Strategy, State
BookMyShow (Movie Ticket Booking)
Hard
Problem Summary
Design a high-concurrency movie ticket reservation system managing multi-screen cinema seats, temporary 10-minute checkout locks, dynamic weekend pricing, and distributed locking.
Functional Scope
- Real-time seat layout map (Available, Locked, Booked, Out of Service).
- Temporary 10-minute seat locking during user payment checkout.
- Automatic unlock daemon resetting seats if payment times out.
- Pluggable dynamic pricing strategy (Weekend surge, Holiday premium, Row category).
- Zero double-booking guarantee under concurrent checkout attempts.
Entity-Relationship (ER) Schema
Cinema [1] --> [*] Screen Screen [1] --> [*] Show Show [1] --> [*] ShowSeat ShowSeat [1] --> [1] SeatStatus Show [1] --> [1] PricingStrategy
Design Approach
Apply a two-phase seat locking protocol with Redis TTL or in-memory timestamps to hold seats for 10 minutes. Use optimistic concurrency or row-level database locks to eliminate race conditions during high-demand movie premieres.
Core Classes & Models
CinemaService (Orchestrator managing show listings and seat locks)Show (Movie, Screen, ShowTime, SeatMap)ShowSeat (SeatNo, SeatCategory: SILVER, GOLD, PLATINUM, Status: AVAILABLE, LOCKED, BOOKED)PricingStrategy (WeekendPricing, PeakHourPricing)SeatLockManager (Distributed Redis/memory lock manager with TTL)
Code Blueprint
102 lines/**
* Low-Level Design: BookMyShow Cinema Ticketing System
* Design Patterns Applied:
* - State Pattern: Seat states (AvailableState, HeldState, BookedState)
* - Strategy Pattern: Dynamic seat pricing (Normal, VIP, Weekend Surge)
* - Observer Pattern: Temporary hold expiry timer and seat release notifications
*/
import java.util.*;
enum SeatType { SILVER, GOLD, PLATINUM }
enum SeatStatus { AVAILABLE, HELD, BOOKED }
// --- [DESIGN PATTERN: Strategy] Seat pricing strategy ---
interface SeatPricingStrategy {
double getPrice(SeatType type, boolean isWeekend);
}
class StandardPricingStrategy implements SeatPricingStrategy {
@Override
public double getPrice(SeatType type, boolean isWeekend) {
double base = switch (type) {
case SILVER -> 10.0;
case GOLD -> 15.0;
case PLATINUM -> 25.0;
};
return isWeekend ? base * 1.25 : base;
}
}
// --- [DESIGN PATTERN: Observer] Seat release notifier ---
interface SeatHoldObserver {
void onSeatReleased(String seatNumber);
}
class Seat {
private final String seatNumber;
private final SeatType type;
private SeatStatus status = SeatStatus.AVAILABLE;
public Seat(String seatNumber, SeatType type) {
this.seatNumber = seatNumber;
this.type = type;
}
public String getSeatNumber() { return seatNumber; }
public SeatType getType() { return type; }
public SeatStatus getStatus() { return status; }
public void setStatus(SeatStatus status) { this.status = status; }
}
class ShowBookingService {
private final Map<String, Seat> seats = new HashMap<>();
private final SeatPricingStrategy pricingStrategy;
private final List<SeatHoldObserver> observers = new ArrayList<>();
public ShowBookingService(SeatPricingStrategy strategy) {
this.pricingStrategy = strategy;
}
public void addSeat(Seat seat) { seats.put(seat.getSeatNumber(), seat); }
public void addObserver(SeatHoldObserver o) { observers.add(o); }
public synchronized boolean holdSeats(List<String> seatNumbers, String userId) {
for (String sn : seatNumbers) {
Seat s = seats.get(sn);
if (s == null || s.getStatus() != SeatStatus.AVAILABLE) {
System.out.println("❌ Seat " + sn + " is already reserved or booked!");
return false;
}
}
for (String sn : seatNumbers) {
seats.get(sn).setStatus(SeatStatus.HELD);
}
System.out.println("⏳ Held seats " + seatNumbers + " for user " + userId + " (10m TTL lock)");
return true;
}
public synchronized boolean confirmBooking(List<String> seatNumbers) {
double total = 0.0;
for (String sn : seatNumbers) {
Seat s = seats.get(sn);
if (s.getStatus() != SeatStatus.HELD) return false;
s.setStatus(SeatStatus.BOOKED);
total += pricingStrategy.getPrice(s.getType(), false);
}
System.out.printf("🎟️ Booking CONFIRMED for seats %s! Total: $%.2f%n", seatNumbers, total);
return true;
}
}
public class Main {
public static void main(String[] args) {
ShowBookingService bookingService = new ShowBookingService(new StandardPricingStrategy());
bookingService.addSeat(new Seat("A1", SeatType.PLATINUM));
bookingService.addSeat(new Seat("A2", SeatType.PLATINUM));
bookingService.addSeat(new Seat("B1", SeatType.GOLD));
System.out.println("--- BookMyShow Reservation Flow ---");
bookingService.holdSeats(List.of("A1", "A2"), "USR-401");
bookingService.confirmBooking(List.of("A1", "A2"));
}
}