Movie Ticket Booking System
Problem Statement
Section titled “Problem Statement”Design a movie ticket booking system (like BookMyShow) that allows users to search for movies, view showtimes, select seats, book tickets, and make payments. The system should handle multiple theaters, screens, movies, and prevent double-booking of seats.
Requirements
Section titled “Requirements”Functional Requirements
Section titled “Functional Requirements”- Browse movies by location, language, and genre
- View theater locations and showtimes
- Display seat layout and availability in real-time
- Select and book seats with temporary hold
- Support multiple seat types (Regular, Premium, VIP)
- Process payments and generate tickets
- Cancel bookings with refund policies
- Send booking confirmations via email/SMS
- Support different movie formats (2D, 3D, IMAX)
- Handle multiple concurrent bookings
Non-Functional Requirements
Section titled “Non-Functional Requirements”- Prevent double-booking with proper locking
- Seat hold timeout mechanism (10 minutes)
- High availability for search operations
- Fast seat availability checks
- Scalable to multiple cities and theaters
Simplified Class Diagram
Section titled “Simplified Class Diagram”Simplified Overview
Section titled “Simplified Overview”Detailed Class Diagram
Section titled “Detailed Class Diagram”Key Design Patterns
Section titled “Key Design Patterns”- Singleton Pattern: BookingService as central coordinator
- Strategy Pattern: Different pricing strategies
- Factory Pattern: Create bookings and tickets
- Observer Pattern: Notifications for bookings
- State Pattern: Seat and booking status management
Design Pattern Diagrams
Section titled “Design Pattern Diagrams”1. Strategy Pattern - Dynamic Pricing
Section titled “1. Strategy Pattern - Dynamic Pricing”2. State Pattern - Seat Locking & Booking
Section titled “2. State Pattern - Seat Locking & Booking”3. Observer Pattern - Booking Notifications
Section titled “3. Observer Pattern - Booking Notifications”Code Snippets
Section titled “Code Snippets”Initialize Screen with Seats
Section titled “Initialize Screen with Seats”public class Screen { public void initializeSeats(int rows, int seatsPerRow) { char rowLabel = 'A';
for (int i = 0; i < rows; i++) { for (int j = 1; j <= seatsPerRow; j++) { String seatNumber = rowLabel + String.valueOf(j); SeatType type;
// Premium seats in middle rows if (i >= rows/3 && i < 2*rows/3) { type = SeatType.PREMIUM; } // VIP seats in back rows else if (i >= 2*rows/3) { type = SeatType.VIP; } // Regular seats in front else { type = SeatType.REGULAR; }
Seat seat = new Seat(UUID.randomUUID().toString(), seatNumber); seat.setRow(String.valueOf(rowLabel)); seat.setColumn(j); seat.setType(type); seat.setStatus(SeatStatus.AVAILABLE); seat.setScreen(this);
seats.add(seat); } rowLabel++; }
this.totalSeats = seats.size(); }}Create Booking with Seat Locking
Section titled “Create Booking with Seat Locking”public class BookingService { private static final int SEAT_HOLD_DURATION_MINUTES = 10;
public synchronized Booking createBooking(User user, Show show, List<Seat> seats) throws BookingException { // Validate show if (show.getStatus() != ShowStatus.SCHEDULED) { throw new BookingException("Show is not available for booking"); }
// Check seat availability for (Seat seat : seats) { if (!seat.isAvailable()) { throw new BookingException("Seat " + seat.getSeatNumber() + " is not available"); } }
// Create booking Booking booking = new Booking(user, show); booking.setStatus(BookingStatus.PENDING); booking.setBookingTime(DateTime.now()); booking.setExpiryTime(DateTime.now().plusMinutes(SEAT_HOLD_DURATION_MINUTES));
// Lock seats if (!lockSeats(seats, booking)) { throw new BookingException("Failed to lock seats"); }
// Add seats to booking for (Seat seat : seats) { booking.addSeat(seat); }
// Calculate total double total = calculateBookingAmount(show, seats); booking.setTotalAmount(total);
// Save booking bookings.put(booking.getBookingId(), booking);
return booking; }
private boolean lockSeats(List<Seat> seats, Booking booking) { DateTime expiryTime = DateTime.now().plusMinutes(SEAT_HOLD_DURATION_MINUTES);
for (Seat seat : seats) { if (seat.getStatus() != SeatStatus.AVAILABLE) { // Rollback previous locks rollbackSeatLocks(booking); return false; }
// Create lock SeatLock lock = new SeatLock(seat, booking); lock.setExpiryTime(expiryTime); seatLocks.put(seat.getSeatId(), lock);
// Update seat status seat.setStatus(SeatStatus.TEMPORARILY_HELD); }
return true; }
private double calculateBookingAmount(Show show, List<Seat> seats) { double total = 0; for (Seat seat : seats) { total += pricingStrategy.calculatePrice(show, seat.getType()); } return total; }}Confirm Booking and Generate Tickets
Section titled “Confirm Booking and Generate Tickets”public class BookingService { public synchronized List<Ticket> confirmBooking(String bookingId, Payment payment) throws BookingException { Booking booking = bookings.get(bookingId);
if (booking == null) { throw new BookingException("Booking not found"); }
if (booking.isExpired()) { releaseSeats(booking); throw new BookingException("Booking has expired"); }
// Process payment if (!payment.process()) { throw new BookingException("Payment processing failed"); }
// Update booking status booking.setPayment(payment); booking.setStatus(BookingStatus.CONFIRMED);
// Book seats permanently for (Seat seat : booking.getSeats()) { seat.setStatus(SeatStatus.BOOKED); // Remove lock seatLocks.remove(seat.getSeatId()); }
// Generate tickets List<Ticket> tickets = new ArrayList<>(); for (Seat seat : booking.getSeats()) { Ticket ticket = new Ticket(booking, seat); double price = pricingStrategy.calculatePrice( booking.getShow(), seat.getType()); ticket.setPrice(price); ticket.setQrCode(ticket.generateQRCode()); tickets.add(ticket); }
// Send confirmation notificationService.sendBookingConfirmation(booking, tickets);
return tickets; }}Cancel Booking
Section titled “Cancel Booking”public class BookingService { public synchronized boolean cancelBooking(String bookingId) throws BookingException { Booking booking = bookings.get(bookingId);
if (booking == null) { throw new BookingException("Booking not found"); }
if (booking.getStatus() != BookingStatus.CONFIRMED) { throw new BookingException("Only confirmed bookings can be cancelled"); }
// Check cancellation policy (e.g., 2 hours before show) DateTime showTime = booking.getShow().getStartTime(); if (DateTime.now().plusHours(2).isAfter(showTime)) { throw new BookingException("Cancellation not allowed within 2 hours of show"); }
// Process refund if (booking.getPayment() != null) { booking.getPayment().refund(); }
// Release seats for (Seat seat : booking.getSeats()) { seat.setStatus(SeatStatus.AVAILABLE); }
// Update booking status booking.setStatus(BookingStatus.CANCELLED);
// Send notification notificationService.sendCancellationNotification(booking);
return true; }
private void releaseSeats(Booking booking) { for (Seat seat : booking.getSeats()) { seat.setStatus(SeatStatus.AVAILABLE); seatLocks.remove(seat.getSeatId()); } }}Release Expired Seat Locks (Background Task)
Section titled “Release Expired Seat Locks (Background Task)”public class BookingService { public void releaseExpiredLocks() { DateTime now = DateTime.now(); List<String> expiredLocks = new ArrayList<>();
synchronized(this) { for (Map.Entry<String, SeatLock> entry : seatLocks.entrySet()) { SeatLock lock = entry.getValue();
if (lock.isExpired()) { // Release seat lock.getSeat().setStatus(SeatStatus.AVAILABLE); expiredLocks.add(entry.getKey());
// Update booking status Booking booking = lock.getBooking(); if (booking.getStatus() == BookingStatus.PENDING) { booking.setStatus(BookingStatus.EXPIRED); } } }
// Remove expired locks for (String lockId : expiredLocks) { seatLocks.remove(lockId); } } }
// This should be called periodically (e.g., every minute) public void startLockCleanupTask() { ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(); executor.scheduleAtFixedRate( this::releaseExpiredLocks, 0, 1, TimeUnit.MINUTES ); }}Search Movies and Shows
Section titled “Search Movies and Shows”public class BookingService { public List<Movie> searchMovies(SearchCriteria criteria) { return movies.values().stream() .filter(movie -> { if (criteria.getMovieName() != null && !movie.getTitle().toLowerCase() .contains(criteria.getMovieName().toLowerCase())) { return false; } if (criteria.getGenre() != null && !movie.getGenre().equalsIgnoreCase(criteria.getGenre())) { return false; } if (criteria.getLanguage() != null && !movie.getLanguage().equalsIgnoreCase(criteria.getLanguage())) { return false; } return true; }) .collect(Collectors.toList()); }
public List<Show> getShows(String movieId, String city, Date date) { return shows.values().stream() .filter(show -> show.getMovie().getMovieId().equals(movieId)) .filter(show -> show.getScreen().getTheater() .getAddress().getCity().equalsIgnoreCase(city)) .filter(show -> isSameDate(show.getStartTime().toDate(), date)) .filter(show -> show.getStatus() == ShowStatus.SCHEDULED) .sorted(Comparator.comparing(Show::getStartTime)) .collect(Collectors.toList()); }
private boolean isSameDate(Date date1, Date date2) { Calendar cal1 = Calendar.getInstance(); Calendar cal2 = Calendar.getInstance(); cal1.setTime(date1); cal2.setTime(date2);
return cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR) && cal1.get(Calendar.MONTH) == cal2.get(Calendar.MONTH) && cal1.get(Calendar.DAY_OF_MONTH) == cal2.get(Calendar.DAY_OF_MONTH); }}Pricing Strategy
Section titled “Pricing Strategy”public class WeekendPricing implements PricingStrategy { private static final double WEEKEND_MULTIPLIER = 1.3; // 30% more private static final Map<SeatType, Double> SEAT_MULTIPLIERS = Map.of( SeatType.REGULAR, 1.0, SeatType.PREMIUM, 1.5, SeatType.VIP, 2.0 );
@Override public double calculatePrice(Show show, SeatType seatType) { double basePrice = show.getBasePrice(); double seatMultiplier = SEAT_MULTIPLIERS.get(seatType);
// Check if weekend Calendar cal = Calendar.getInstance(); cal.setTime(show.getStartTime().toDate()); int dayOfWeek = cal.get(Calendar.DAY_OF_WEEK);
double price = basePrice * seatMultiplier;
if (dayOfWeek == Calendar.SATURDAY || dayOfWeek == Calendar.SUNDAY) { price *= WEEKEND_MULTIPLIER; }
return price; }}Extension Points
Section titled “Extension Points”- Add food and beverage ordering
- Implement seat recommendations based on preferences
- Add loyalty programs and discounts
- Support group bookings with special pricing
- Implement waitlist for sold-out shows
- Add movie trailers and reviews integration
- Support multiple languages for interface
- Implement dynamic pricing based on demand
- Add theater facility information (parking, accessibility)
- Support gift cards and vouchers