Limited time: AI code review, hints, mock interviews, whiteboard analysis, and all Pro features are unlocked. Enroll
โฑ๏ธ 24 min read

Designing a Delivery Slot Booking System

Difficulty: Intermediate Patterns: Template Method, Observer, Factory Asked at: Licious, BigBasket, Blinkit, Zepto


Functional Requirements

  1. Create time slots - define delivery windows with start time, end time, and max capacity
  2. Book slots with capacity - customers book a slot; booking succeeds only if capacity available
  3. Cancel bookings - customers can cancel; freed capacity becomes available again
  4. Check availability - query available slots for a given date/time range
  5. Conflict detection - prevent double-booking by same customer in overlapping slots
  6. Waitlist - when a slot is full, customers can join a waitlist and get auto-promoted on cancellation

Non-Functional Requirements

  1. Thread-safety - concurrent bookings must not exceed slot capacity
  2. Fairness - waitlist is FIFO; first to join gets promoted first
  3. Extensibility - new slot types (express, scheduled, same-day) without changing booking logic

Core Entities

Entity Description
TimeSlot Delivery window: start, end, capacity, current bookings
SlotType Enum: STANDARD, EXPRESS, SAME_DAY
Booking Links a customer to a slot with status (CONFIRMED, WAITLISTED, CANCELLED)
BookingStatus Enum: CONFIRMED, WAITLISTED, CANCELLED, PROMOTED
Customer ID, name, address
WaitlistEntry Customer + timestamp in waitlist queue
SlotManager Creates and manages all time slots
BookingService Orchestrates booking, cancellation, waitlist promotion
BookingObserver Notified on booking events (confirmed, cancelled, promoted)
SlotFactory Creates different types of slots with appropriate defaults

Class Diagram

classDiagram
    class SlotType {
        <<enumeration>>
        STANDARD
        EXPRESS
        SAME_DAY
    }

    class BookingStatus {
        <<enumeration>>
        CONFIRMED
        WAITLISTED
        CANCELLED
        PROMOTED
    }

    class TimeSlot {
        -String id
        -LocalDateTime startTime
        -LocalDateTime endTime
        -int maxCapacity
        -int currentBookings
        -SlotType type
        -Queue~WaitlistEntry~ waitlist
        +isAvailable() boolean
        +book() boolean
        +release()
        +addToWaitlist(Customer)
        +promoteFromWaitlist() Customer
    }

    class Customer {
        -String id
        -String name
        -String address
    }

    class Booking {
        -String id
        -Customer customer
        -TimeSlot slot
        -BookingStatus status
        -LocalDateTime bookedAt
        +confirm()
        +cancel()
        +waitlist()
    }

    class WaitlistEntry {
        -Customer customer
        -long timestamp
    }

    class SlotFactory {
        +createSlot(SlotType, LocalDateTime start, LocalDateTime end) TimeSlot
    }

    class BookingObserver {
        <<interface>>
        +onBookingConfirmed(Booking)
        +onBookingCancelled(Booking)
        +onCustomerPromoted(Booking)
        +onWaitlisted(Booking)
    }

    class BookingService {
        -Map~String, TimeSlot~ slots
        -Map~String, Booking~ bookings
        -Map~String, List~Booking~~ customerBookings
        -List~BookingObserver~ observers
        +bookSlot(Customer, String slotId) Booking
        +cancelBooking(String bookingId)
        +getAvailableSlots(LocalDateTime from, LocalDateTime to) List
        +getWaitlistPosition(String bookingId) int
    }

    BookingService --> TimeSlot
    BookingService --> Booking
    BookingService --> BookingObserver
    Booking --> Customer
    Booking --> TimeSlot
    Booking --> BookingStatus
    TimeSlot --> SlotType
    TimeSlot --> WaitlistEntry
    SlotFactory --> TimeSlot

Design Patterns

Pattern Where Why
Template Method Booking flow: validate โ†’ reserve โ†’ notify (subclass can override validation) Common booking structure with customizable steps per slot type
Observer BookingObserver notified on confirm/cancel/promote Decouple notifications (SMS, email, push) from booking logic
Factory SlotFactory creates STANDARD/EXPRESS/SAME_DAY slots Encapsulate creation logic; each type has different capacity defaults

Data Structures

Component Structure Why
Slots ConcurrentHashMap<String, TimeSlot> O(1) slot lookup by ID
Bookings ConcurrentHashMap<String, Booking> O(1) booking lookup
Customer bookings Map<String, List<Booking>> Track all bookings per customer for conflict detection
Waitlist per slot LinkedList<WaitlistEntry> (FIFO queue) Fair ordering; O(1) add/remove from ends
Available slots Filtered from all slots by time range Linear scan acceptable for bounded slot count

How It All Fits Together

Hereโ€™s the complete flow for booking a delivery slot:

  1. Customer selects a time window โ†’ calls bookSlot(customer, slotId)
  2. BookingService validates: does the slot exist? Is the customer already booked in an overlapping slot?
  3. If slot has capacity: decrement available count, create Booking with status CONFIRMED
  4. If slot is full: create Booking with status WAITLISTED, add to slotโ€™s waitlist queue
  5. Observers notified (SMS/push sent to customer)

When a booking is cancelled:

  1. Customer calls cancelBooking(bookingId)
  2. Booking status โ†’ CANCELLED, slot capacity released (+1)
  3. If waitlist is non-empty: promote first customer โ†’ auto-confirm their booking
  4. Promoted customerโ€™s booking status โ†’ PROMOTED (then CONFIRMED)
  5. Observers notified for both cancellation and promotion

Complete Code

Models and Enums

import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.locks.*;
import java.time.*;
import java.time.format.*;

// โ”€โ”€โ”€ Enums โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
enum SlotType {
    STANDARD, EXPRESS, SAME_DAY
}

enum BookingStatus {
    CONFIRMED, WAITLISTED, CANCELLED, PROMOTED
}

// โ”€โ”€โ”€ Customer โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
class Customer {
    private final String id;
    private final String name;
    private final String address;

    public Customer(String id, String name, String address) {
        this.id = id;
        this.name = name;
        this.address = address;
    }

    public String getId() { return id; }
    public String getName() { return name; }
    public String getAddress() { return address; }

    @Override
    public String toString() { return "Customer[" + name + "]"; }
}

// โ”€โ”€โ”€ Waitlist Entry โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
class WaitlistEntry {
    private final Customer customer;
    private final long timestamp;

    public WaitlistEntry(Customer customer) {
        this.customer = customer;
        this.timestamp = System.currentTimeMillis();
    }

    public Customer getCustomer() { return customer; }
    public long getTimestamp() { return timestamp; }
}

// โ”€โ”€โ”€ Time Slot โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
class TimeSlot {
    private final String id;
    private final LocalDateTime startTime;
    private final LocalDateTime endTime;
    private final int maxCapacity;
    private int currentBookings;
    private final SlotType type;
    private final Queue<WaitlistEntry> waitlist;

    public TimeSlot(String id, LocalDateTime start, LocalDateTime end,
                    int maxCapacity, SlotType type) {
        this.id = id;
        this.startTime = start;
        this.endTime = end;
        this.maxCapacity = maxCapacity;
        this.currentBookings = 0;
        this.type = type;
        this.waitlist = new LinkedList<>();
    }

    public boolean isAvailable() {
        return currentBookings < maxCapacity;
    }

    public boolean book() {
        if (!isAvailable()) return false;
        currentBookings++;
        return true;
    }

    public void release() {
        if (currentBookings > 0) currentBookings--;
    }

    public void addToWaitlist(Customer customer) {
        waitlist.offer(new WaitlistEntry(customer));
    }

    public Customer promoteFromWaitlist() {
        WaitlistEntry entry = waitlist.poll();
        return entry != null ? entry.getCustomer() : null;
    }

    public int getWaitlistSize() { return waitlist.size(); }

    public boolean overlaps(TimeSlot other) {
        return this.startTime.isBefore(other.endTime) &&
               other.startTime.isBefore(this.endTime);
    }

    public String getId() { return id; }
    public LocalDateTime getStartTime() { return startTime; }
    public LocalDateTime getEndTime() { return endTime; }
    public int getMaxCapacity() { return maxCapacity; }
    public int getCurrentBookings() { return currentBookings; }
    public int getAvailableCapacity() { return maxCapacity - currentBookings; }
    public SlotType getType() { return type; }

    @Override
    public String toString() {
        DateTimeFormatter fmt = DateTimeFormatter.ofPattern("HH:mm");
        return "Slot[" + id + " | " + type + " | " +
               startTime.format(fmt) + "-" + endTime.format(fmt) +
               " | " + getAvailableCapacity() + "/" + maxCapacity + " available" +
               (waitlist.isEmpty() ? "" : " | waitlist=" + waitlist.size()) + "]";
    }
}

// โ”€โ”€โ”€ Booking โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
class Booking {
    private final String id;
    private final Customer customer;
    private final TimeSlot slot;
    private BookingStatus status;
    private final LocalDateTime bookedAt;

    public Booking(Customer customer, TimeSlot slot, BookingStatus status) {
        this.id = "BKG-" + UUID.randomUUID().toString().substring(0, 6);
        this.customer = customer;
        this.slot = slot;
        this.status = status;
        this.bookedAt = LocalDateTime.now();
    }

    public void confirm() { this.status = BookingStatus.CONFIRMED; }
    public void cancel() { this.status = BookingStatus.CANCELLED; }
    public void waitlist() { this.status = BookingStatus.WAITLISTED; }
    public void promote() { this.status = BookingStatus.PROMOTED; }

    public String getId() { return id; }
    public Customer getCustomer() { return customer; }
    public TimeSlot getSlot() { return slot; }
    public BookingStatus getStatus() { return status; }

    @Override
    public String toString() {
        DateTimeFormatter fmt = DateTimeFormatter.ofPattern("HH:mm");
        return "Booking[" + id + " | " + customer.getName() + " | " +
               slot.getStartTime().format(fmt) + "-" + slot.getEndTime().format(fmt) +
               " | " + status + "]";
    }
}

// โ”€โ”€โ”€ Slot Factory โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
class SlotFactory {
    private static int counter = 0;

    public static TimeSlot createSlot(SlotType type, LocalDateTime start, LocalDateTime end) {
        int capacity;
        switch (type) {
            case EXPRESS: capacity = 5; break;
            case SAME_DAY: capacity = 10; break;
            default: capacity = 20; break;
        }
        return new TimeSlot("SLOT-" + (++counter), start, end, capacity, type);
    }

    public static TimeSlot createSlot(SlotType type, LocalDateTime start,
                                       LocalDateTime end, int capacity) {
        return new TimeSlot("SLOT-" + (++counter), start, end, capacity, type);
    }
}

// โ”€โ”€โ”€ Observer Interface โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
interface BookingObserver {
    void onBookingConfirmed(Booking booking);
    void onBookingCancelled(Booking booking);
    void onCustomerPromoted(Booking booking);
    void onWaitlisted(Booking booking);
}

// โ”€โ”€โ”€ Notification Observer โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
class NotificationObserver implements BookingObserver {
    @Override
    public void onBookingConfirmed(Booking b) {
        System.out.println("  [NOTIFY] Booking confirmed for " + b.getCustomer().getName() +
                          " - " + b.getSlot().getType() + " slot");
    }
    @Override
    public void onBookingCancelled(Booking b) {
        System.out.println("  [NOTIFY] Booking cancelled for " + b.getCustomer().getName());
    }
    @Override
    public void onCustomerPromoted(Booking b) {
        System.out.println("  [NOTIFY] " + b.getCustomer().getName() +
                          " promoted from waitlist! Booking confirmed.");
    }
    @Override
    public void onWaitlisted(Booking b) {
        System.out.println("  [NOTIFY] " + b.getCustomer().getName() +
                          " added to waitlist (position: " +
                          b.getSlot().getWaitlistSize() + ")");
    }
}

// โ”€โ”€โ”€ Booking Service โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
class BookingService {
    private final ConcurrentHashMap<String, TimeSlot> slots;
    private final ConcurrentHashMap<String, Booking> bookings;
    private final ConcurrentHashMap<String, List<Booking>> customerBookings;
    private final List<BookingObserver> observers;
    private final ReentrantLock lock;

    public BookingService() {
        this.slots = new ConcurrentHashMap<>();
        this.bookings = new ConcurrentHashMap<>();
        this.customerBookings = new ConcurrentHashMap<>();
        this.observers = new CopyOnWriteArrayList<>();
        this.lock = new ReentrantLock();
    }

    public void addObserver(BookingObserver observer) {
        observers.add(observer);
    }

    public void addSlot(TimeSlot slot) {
        slots.put(slot.getId(), slot);
    }

    public Booking bookSlot(Customer customer, String slotId) {
        lock.lock();
        try {
            TimeSlot slot = slots.get(slotId);
            if (slot == null) {
                throw new RuntimeException("Slot not found: " + slotId);
            }

            // Conflict detection: check overlapping bookings for this customer
            if (hasConflict(customer, slot)) {
                throw new RuntimeException("Customer " + customer.getName() +
                    " already has a booking in an overlapping time slot");
            }

            Booking booking;
            if (slot.isAvailable()) {
                slot.book();
                booking = new Booking(customer, slot, BookingStatus.CONFIRMED);
                observers.forEach(o -> o.onBookingConfirmed(booking));
            } else {
                // Add to waitlist
                slot.addToWaitlist(customer);
                booking = new Booking(customer, slot, BookingStatus.WAITLISTED);
                observers.forEach(o -> o.onWaitlisted(booking));
            }

            bookings.put(booking.getId(), booking);
            customerBookings.computeIfAbsent(customer.getId(), k -> new ArrayList<>())
                           .add(booking);
            return booking;
        } finally {
            lock.unlock();
        }
    }

    public void cancelBooking(String bookingId) {
        lock.lock();
        try {
            Booking booking = bookings.get(bookingId);
            if (booking == null) {
                throw new RuntimeException("Booking not found: " + bookingId);
            }
            if (booking.getStatus() == BookingStatus.CANCELLED) {
                throw new RuntimeException("Booking already cancelled");
            }

            BookingStatus previousStatus = booking.getStatus();
            booking.cancel();
            observers.forEach(o -> o.onBookingCancelled(booking));

            if (previousStatus == BookingStatus.CONFIRMED) {
                TimeSlot slot = booking.getSlot();
                slot.release();

                // Promote from waitlist
                Customer promoted = slot.promoteFromWaitlist();
                if (promoted != null) {
                    slot.book();
                    // Find the waitlisted booking for this customer
                    Booking promotedBooking = findWaitlistedBooking(promoted, slot);
                    if (promotedBooking != null) {
                        promotedBooking.confirm();
                        observers.forEach(o -> o.onCustomerPromoted(promotedBooking));
                    }
                }
            }
        } finally {
            lock.unlock();
        }
    }

    private Booking findWaitlistedBooking(Customer customer, TimeSlot slot) {
        List<Booking> custBookings = customerBookings.get(customer.getId());
        if (custBookings == null) return null;
        return custBookings.stream()
            .filter(b -> b.getSlot().getId().equals(slot.getId()))
            .filter(b -> b.getStatus() == BookingStatus.WAITLISTED)
            .findFirst()
            .orElse(null);
    }

    private boolean hasConflict(Customer customer, TimeSlot targetSlot) {
        List<Booking> existing = customerBookings.get(customer.getId());
        if (existing == null) return false;
        return existing.stream()
            .filter(b -> b.getStatus() == BookingStatus.CONFIRMED)
            .anyMatch(b -> b.getSlot().overlaps(targetSlot));
    }

    public List<TimeSlot> getAvailableSlots(LocalDateTime from, LocalDateTime to) {
        List<TimeSlot> available = new ArrayList<>();
        for (TimeSlot slot : slots.values()) {
            if (!slot.getStartTime().isBefore(from) &&
                !slot.getEndTime().isAfter(to) &&
                slot.isAvailable()) {
                available.add(slot);
            }
        }
        available.sort(Comparator.comparing(TimeSlot::getStartTime));
        return available;
    }

    public List<TimeSlot> getAllSlots() {
        List<TimeSlot> all = new ArrayList<>(slots.values());
        all.sort(Comparator.comparing(TimeSlot::getStartTime));
        return all;
    }

    public int getWaitlistPosition(String bookingId) {
        Booking booking = bookings.get(bookingId);
        if (booking == null || booking.getStatus() != BookingStatus.WAITLISTED) return -1;
        // Position is approximate since we can't peek into LinkedList efficiently
        return booking.getSlot().getWaitlistSize();
    }

    public void displaySlots() {
        System.out.println("\n--- All Delivery Slots ---");
        for (TimeSlot slot : getAllSlots()) {
            System.out.println("  " + slot);
        }
    }
}

// โ”€โ”€โ”€ Main Demo โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
public class DeliverySlotBookingDemo {
    public static void main(String[] args) {
        System.out.println("โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•");
        System.out.println("   DELIVERY SLOT BOOKING - LLD DEMO   ");
        System.out.println("โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•\n");

        BookingService service = new BookingService();
        service.addObserver(new NotificationObserver());

        // โ”€โ”€โ”€ Create Slots โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
        System.out.println("--- Creating Delivery Slots ---");
        LocalDate today = LocalDate.now();

        TimeSlot morning = SlotFactory.createSlot(SlotType.STANDARD,
            today.atTime(9, 0), today.atTime(11, 0), 3);
        TimeSlot midday = SlotFactory.createSlot(SlotType.EXPRESS,
            today.atTime(11, 0), today.atTime(13, 0), 2);
        TimeSlot afternoon = SlotFactory.createSlot(SlotType.STANDARD,
            today.atTime(14, 0), today.atTime(16, 0), 3);
        TimeSlot evening = SlotFactory.createSlot(SlotType.SAME_DAY,
            today.atTime(18, 0), today.atTime(21, 0), 2);

        service.addSlot(morning);
        service.addSlot(midday);
        service.addSlot(afternoon);
        service.addSlot(evening);
        service.displaySlots();

        // โ”€โ”€โ”€ Create Customers โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
        Customer alice = new Customer("C1", "Alice", "123 Main St");
        Customer bob = new Customer("C2", "Bob", "456 Oak Ave");
        Customer charlie = new Customer("C3", "Charlie", "789 Pine Rd");
        Customer diana = new Customer("C4", "Diana", "321 Elm St");
        Customer eve = new Customer("C5", "Eve", "654 Maple Dr");

        // โ”€โ”€โ”€ Book Slots โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
        System.out.println("\n--- Booking Slots ---");
        Booking b1 = service.bookSlot(alice, morning.getId());
        System.out.println("  " + b1);

        Booking b2 = service.bookSlot(bob, morning.getId());
        System.out.println("  " + b2);

        Booking b3 = service.bookSlot(charlie, morning.getId());
        System.out.println("  " + b3);

        // Morning is now full (capacity=3) - next booking goes to waitlist
        System.out.println("\n--- Morning Slot Full - Waitlist ---");
        Booking b4 = service.bookSlot(diana, morning.getId());
        System.out.println("  " + b4);

        Booking b5 = service.bookSlot(eve, morning.getId());
        System.out.println("  " + b5);

        service.displaySlots();

        // โ”€โ”€โ”€ Cancel and Promote โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
        System.out.println("\n--- Cancel Alice's Booking (auto-promote Diana) ---");
        service.cancelBooking(b1.getId());
        System.out.println("  Alice's booking: " + b1.getStatus());
        System.out.println("  Diana's booking: " + b4.getStatus());

        service.displaySlots();

        // โ”€โ”€โ”€ Conflict Detection โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
        System.out.println("\n--- Conflict Detection ---");
        // Bob already has morning slot - try overlapping
        try {
            // Create an overlapping slot for testing
            TimeSlot overlap = SlotFactory.createSlot(SlotType.EXPRESS,
                today.atTime(10, 0), today.atTime(12, 0), 5);
            service.addSlot(overlap);
            service.bookSlot(bob, overlap.getId());
        } catch (RuntimeException e) {
            System.out.println("  Expected: " + e.getMessage());
        }

        // โ”€โ”€โ”€ Check Availability โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
        System.out.println("\n--- Available Slots (14:00 - 21:00) ---");
        List<TimeSlot> available = service.getAvailableSlots(
            today.atTime(14, 0), today.atTime(21, 0));
        for (TimeSlot slot : available) {
            System.out.println("  " + slot);
        }

        // โ”€โ”€โ”€ Express Slot Booking โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
        System.out.println("\n--- Express Slot Booking ---");
        Booking b6 = service.bookSlot(alice, midday.getId());
        System.out.println("  " + b6);
        Booking b7 = service.bookSlot(charlie, midday.getId());
        System.out.println("  " + b7);

        // Express now full
        Booking b8 = service.bookSlot(eve, midday.getId());
        System.out.println("  " + b8); // waitlisted

        // โ”€โ”€โ”€ Cancel from Express Slot โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
        System.out.println("\n--- Cancel Charlie from Express (promote Eve) ---");
        service.cancelBooking(b7.getId());

        service.displaySlots();

        System.out.println("\nโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•");
        System.out.println("           DEMO COMPLETE               ");
        System.out.println("โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•");
    }
}
import threading
import uuid
from datetime import datetime, timedelta
from collections import deque
from enum import Enum
from abc import ABC, abstractmethod
from typing import Optional

# โ”€โ”€โ”€ Enums โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
class SlotType(Enum):
    STANDARD = "STANDARD"
    EXPRESS = "EXPRESS"
    SAME_DAY = "SAME_DAY"

class BookingStatus(Enum):
    CONFIRMED = "CONFIRMED"
    WAITLISTED = "WAITLISTED"
    CANCELLED = "CANCELLED"
    PROMOTED = "PROMOTED"

# โ”€โ”€โ”€ Customer โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
class Customer:
    def __init__(self, customer_id: str, name: str, address: str):
        self.id = customer_id
        self.name = name
        self.address = address

    def __str__(self) -> str:
        return f"Customer[{self.name}]"

# โ”€โ”€โ”€ Waitlist Entry โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
class WaitlistEntry:
    def __init__(self, customer: Customer):
        self.customer = customer
        self.timestamp = datetime.now()

# โ”€โ”€โ”€ Time Slot โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
class TimeSlot:
    _counter = 0

    def __init__(self, slot_id: str, start_time: datetime, end_time: datetime,
                 max_capacity: int, slot_type: SlotType):
        self.id = slot_id
        self.start_time = start_time
        self.end_time = end_time
        self.max_capacity = max_capacity
        self.current_bookings = 0
        self.type = slot_type
        self.waitlist: deque[WaitlistEntry] = deque()

    @property
    def is_available(self) -> bool:
        return self.current_bookings < self.max_capacity

    @property
    def available_capacity(self) -> int:
        return self.max_capacity - self.current_bookings

    def book(self) -> bool:
        if not self.is_available:
            return False
        self.current_bookings += 1
        return True

    def release(self):
        if self.current_bookings > 0:
            self.current_bookings -= 1

    def add_to_waitlist(self, customer: Customer):
        self.waitlist.append(WaitlistEntry(customer))

    def promote_from_waitlist(self) -> Optional[Customer]:
        if self.waitlist:
            entry = self.waitlist.popleft()
            return entry.customer
        return None

    @property
    def waitlist_size(self) -> int:
        return len(self.waitlist)

    def overlaps(self, other: "TimeSlot") -> bool:
        return self.start_time < other.end_time and other.start_time < self.end_time

    def __str__(self) -> str:
        fmt = "%H:%M"
        wl = f" | waitlist={self.waitlist_size}" if self.waitlist else ""
        return (f"Slot[{self.id} | {self.type.value} | "
                f"{self.start_time.strftime(fmt)}-{self.end_time.strftime(fmt)} | "
                f"{self.available_capacity}/{self.max_capacity} available{wl}]")

# โ”€โ”€โ”€ Booking โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
class Booking:
    def __init__(self, customer: Customer, slot: TimeSlot, status: BookingStatus):
        self.id = f"BKG-{uuid.uuid4().hex[:6]}"
        self.customer = customer
        self.slot = slot
        self.status = status
        self.booked_at = datetime.now()

    def confirm(self): self.status = BookingStatus.CONFIRMED
    def cancel(self): self.status = BookingStatus.CANCELLED
    def waitlisted(self): self.status = BookingStatus.WAITLISTED
    def promote(self): self.status = BookingStatus.PROMOTED

    def __str__(self) -> str:
        fmt = "%H:%M"
        return (f"Booking[{self.id} | {self.customer.name} | "
                f"{self.slot.start_time.strftime(fmt)}-{self.slot.end_time.strftime(fmt)} | "
                f"{self.status.value}]")

# โ”€โ”€โ”€ Slot Factory โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
class SlotFactory:
    _counter = 0

    @classmethod
    def create_slot(cls, slot_type: SlotType, start: datetime, end: datetime,
                    capacity: Optional[int] = None) -> TimeSlot:
        cls._counter += 1
        if capacity is None:
            capacity = {SlotType.STANDARD: 20, SlotType.EXPRESS: 5, SlotType.SAME_DAY: 10}[slot_type]
        return TimeSlot(f"SLOT-{cls._counter}", start, end, capacity, slot_type)

# โ”€โ”€โ”€ Observer Interface โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
class BookingObserver(ABC):
    @abstractmethod
    def on_booking_confirmed(self, booking: Booking): pass
    @abstractmethod
    def on_booking_cancelled(self, booking: Booking): pass
    @abstractmethod
    def on_customer_promoted(self, booking: Booking): pass
    @abstractmethod
    def on_waitlisted(self, booking: Booking): pass

class NotificationObserver(BookingObserver):
    def on_booking_confirmed(self, b):
        print(f"  [NOTIFY] Booking confirmed for {b.customer.name} - {b.slot.type.value} slot")
    def on_booking_cancelled(self, b):
        print(f"  [NOTIFY] Booking cancelled for {b.customer.name}")
    def on_customer_promoted(self, b):
        print(f"  [NOTIFY] {b.customer.name} promoted from waitlist! Booking confirmed.")
    def on_waitlisted(self, b):
        print(f"  [NOTIFY] {b.customer.name} added to waitlist (position: {b.slot.waitlist_size})")

# โ”€โ”€โ”€ Booking Service โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
class BookingService:
    def __init__(self):
        self._slots: dict[str, TimeSlot] = {}
        self._bookings: dict[str, Booking] = {}
        self._customer_bookings: dict[str, list[Booking]] = {}
        self._observers: list[BookingObserver] = []
        self._lock = threading.Lock()

    def add_observer(self, observer: BookingObserver):
        self._observers.append(observer)

    def add_slot(self, slot: TimeSlot):
        self._slots[slot.id] = slot

    def book_slot(self, customer: Customer, slot_id: str) -> Booking:
        with self._lock:
            slot = self._slots.get(slot_id)
            if not slot:
                raise RuntimeError(f"Slot not found: {slot_id}")

            if self._has_conflict(customer, slot):
                raise RuntimeError(
                    f"Customer {customer.name} already has a booking in an overlapping time slot")

            if slot.is_available:
                slot.book()
                booking = Booking(customer, slot, BookingStatus.CONFIRMED)
                for obs in self._observers:
                    obs.on_booking_confirmed(booking)
            else:
                slot.add_to_waitlist(customer)
                booking = Booking(customer, slot, BookingStatus.WAITLISTED)
                for obs in self._observers:
                    obs.on_waitlisted(booking)

            self._bookings[booking.id] = booking
            self._customer_bookings.setdefault(customer.id, []).append(booking)
            return booking

    def cancel_booking(self, booking_id: str):
        with self._lock:
            booking = self._bookings.get(booking_id)
            if not booking:
                raise RuntimeError(f"Booking not found: {booking_id}")
            if booking.status == BookingStatus.CANCELLED:
                raise RuntimeError("Booking already cancelled")

            prev_status = booking.status
            booking.cancel()
            for obs in self._observers:
                obs.on_booking_cancelled(booking)

            if prev_status == BookingStatus.CONFIRMED:
                slot = booking.slot
                slot.release()

                promoted_customer = slot.promote_from_waitlist()
                if promoted_customer:
                    slot.book()
                    promoted_booking = self._find_waitlisted_booking(promoted_customer, slot)
                    if promoted_booking:
                        promoted_booking.confirm()
                        for obs in self._observers:
                            obs.on_customer_promoted(promoted_booking)

    def _find_waitlisted_booking(self, customer: Customer, slot: TimeSlot) -> Optional[Booking]:
        cust_bookings = self._customer_bookings.get(customer.id, [])
        for b in cust_bookings:
            if b.slot.id == slot.id and b.status == BookingStatus.WAITLISTED:
                return b
        return None

    def _has_conflict(self, customer: Customer, target_slot: TimeSlot) -> bool:
        existing = self._customer_bookings.get(customer.id, [])
        return any(
            b.status == BookingStatus.CONFIRMED and b.slot.overlaps(target_slot)
            for b in existing
        )

    def get_available_slots(self, from_time: datetime, to_time: datetime) -> list[TimeSlot]:
        available = [
            s for s in self._slots.values()
            if s.start_time >= from_time and s.end_time <= to_time and s.is_available
        ]
        return sorted(available, key=lambda s: s.start_time)

    def display_slots(self):
        print("\n--- All Delivery Slots ---")
        for slot in sorted(self._slots.values(), key=lambda s: s.start_time):
            print(f"  {slot}")

# โ”€โ”€โ”€ Main Demo โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
def main():
    print("โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•")
    print("   DELIVERY SLOT BOOKING - LLD DEMO   ")
    print("โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•\n")

    service = BookingService()
    service.add_observer(NotificationObserver())

    print("--- Creating Delivery Slots ---")
    today = datetime.now().replace(hour=0, minute=0, second=0, microsecond=0)

    morning = SlotFactory.create_slot(SlotType.STANDARD,
        today.replace(hour=9), today.replace(hour=11), 3)
    midday = SlotFactory.create_slot(SlotType.EXPRESS,
        today.replace(hour=11), today.replace(hour=13), 2)
    afternoon = SlotFactory.create_slot(SlotType.STANDARD,
        today.replace(hour=14), today.replace(hour=16), 3)
    evening = SlotFactory.create_slot(SlotType.SAME_DAY,
        today.replace(hour=18), today.replace(hour=21), 2)

    for slot in [morning, midday, afternoon, evening]:
        service.add_slot(slot)
    service.display_slots()

    alice = Customer("C1", "Alice", "123 Main St")
    bob = Customer("C2", "Bob", "456 Oak Ave")
    charlie = Customer("C3", "Charlie", "789 Pine Rd")
    diana = Customer("C4", "Diana", "321 Elm St")
    eve = Customer("C5", "Eve", "654 Maple Dr")

    print("\n--- Booking Slots ---")
    b1 = service.book_slot(alice, morning.id)
    print(f"  {b1}")
    b2 = service.book_slot(bob, morning.id)
    print(f"  {b2}")
    b3 = service.book_slot(charlie, morning.id)
    print(f"  {b3}")

    print("\n--- Morning Slot Full - Waitlist ---")
    b4 = service.book_slot(diana, morning.id)
    print(f"  {b4}")
    b5 = service.book_slot(eve, morning.id)
    print(f"  {b5}")

    service.display_slots()

    print("\n--- Cancel Alice's Booking (auto-promote Diana) ---")
    service.cancel_booking(b1.id)
    print(f"  Alice's booking: {b1.status.value}")
    print(f"  Diana's booking: {b4.status.value}")

    service.display_slots()

    print("\n--- Conflict Detection ---")
    try:
        overlap = SlotFactory.create_slot(SlotType.EXPRESS,
            today.replace(hour=10), today.replace(hour=12), 5)
        service.add_slot(overlap)
        service.book_slot(bob, overlap.id)
    except RuntimeError as e:
        print(f"  Expected: {e}")

    print("\n--- Available Slots (14:00 - 21:00) ---")
    available = service.get_available_slots(today.replace(hour=14), today.replace(hour=21))
    for slot in available:
        print(f"  {slot}")

    print("\n--- Express Slot Booking ---")
    b6 = service.book_slot(alice, midday.id)
    print(f"  {b6}")
    b7 = service.book_slot(charlie, midday.id)
    print(f"  {b7}")
    b8 = service.book_slot(eve, midday.id)
    print(f"  {b8}")

    print("\n--- Cancel Charlie from Express (promote Eve) ---")
    service.cancel_booking(b7.id)

    service.display_slots()

    print("\nโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•")
    print("           DEMO COMPLETE               ")
    print("โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•")

if __name__ == "__main__":
    main()
#include <iostream>
#include <string>
#include <unordered_map>
#include <vector>
#include <queue>
#include <mutex>
#include <memory>
#include <algorithm>
#include <iomanip>
#include <sstream>
#include <stdexcept>
#include <random>

// โ”€โ”€โ”€ Enums โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
enum class SlotType { STANDARD, EXPRESS, SAME_DAY };
enum class BookingStatus { CONFIRMED, WAITLISTED, CANCELLED, PROMOTED };

std::string slotTypeStr(SlotType t) {
    switch (t) {
        case SlotType::STANDARD: return "STANDARD";
        case SlotType::EXPRESS: return "EXPRESS";
        case SlotType::SAME_DAY: return "SAME_DAY";
    }
    return "";
}

std::string bookingStatusStr(BookingStatus s) {
    switch (s) {
        case BookingStatus::CONFIRMED: return "CONFIRMED";
        case BookingStatus::WAITLISTED: return "WAITLISTED";
        case BookingStatus::CANCELLED: return "CANCELLED";
        case BookingStatus::PROMOTED: return "PROMOTED";
    }
    return "";
}

std::string genBookingId() {
    static std::mt19937 gen(std::random_device{}());
    static std::uniform_int_distribution<int> dist(0, 15);
    static const char* hex = "0123456789abcdef";
    std::string id = "BKG-";
    for (int i = 0; i < 6; ++i) id += hex[dist(gen)];
    return id;
}

// โ”€โ”€โ”€ Simple Time Representation โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
struct SlotTime {
    int hour, minute;
    bool operator<(const SlotTime& o) const {
        return hour * 60 + minute < o.hour * 60 + o.minute;
    }
    bool operator>=(const SlotTime& o) const { return !(*this < o); }
    bool operator<=(const SlotTime& o) const { return !(o < *this); }
    std::string str() const {
        std::ostringstream ss;
        ss << std::setfill('0') << std::setw(2) << hour << ":"
           << std::setfill('0') << std::setw(2) << minute;
        return ss.str();
    }
};

// โ”€โ”€โ”€ Customer โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
struct Customer {
    std::string id, name, address;
};

// โ”€โ”€โ”€ Time Slot โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
class TimeSlot {
public:
    std::string id;
    SlotTime startTime, endTime;
    int maxCapacity, currentBookings = 0;
    SlotType type;
    std::queue<Customer*> waitlist;

    TimeSlot(std::string id, SlotTime start, SlotTime end, int cap, SlotType type)
        : id(std::move(id)), startTime(start), endTime(end), maxCapacity(cap), type(type) {}

    bool isAvailable() const { return currentBookings < maxCapacity; }
    int availableCapacity() const { return maxCapacity - currentBookings; }

    bool book() {
        if (!isAvailable()) return false;
        currentBookings++;
        return true;
    }

    void release() { if (currentBookings > 0) currentBookings--; }

    void addToWaitlist(Customer* c) { waitlist.push(c); }

    Customer* promoteFromWaitlist() {
        if (waitlist.empty()) return nullptr;
        Customer* c = waitlist.front();
        waitlist.pop();
        return c;
    }

    bool overlaps(const TimeSlot& other) const {
        return startTime < other.endTime && other.startTime < endTime;
    }

    std::string toString() const {
        std::ostringstream ss;
        ss << "Slot[" << id << " | " << slotTypeStr(type) << " | "
           << startTime.str() << "-" << endTime.str() << " | "
           << availableCapacity() << "/" << maxCapacity << " available";
        if (!waitlist.empty()) ss << " | waitlist=" << waitlist.size();
        ss << "]";
        return ss.str();
    }
};

// โ”€โ”€โ”€ Booking โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
struct Booking {
    std::string id;
    Customer* customer;
    TimeSlot* slot;
    BookingStatus status;

    Booking(Customer* c, TimeSlot* s, BookingStatus st)
        : id(genBookingId()), customer(c), slot(s), status(st) {}

    std::string toString() const {
        return "Booking[" + id + " | " + customer->name + " | " +
               slot->startTime.str() + "-" + slot->endTime.str() + " | " +
               bookingStatusStr(status) + "]";
    }
};

// โ”€โ”€โ”€ Slot Factory โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
class SlotFactory {
    static int counter;
public:
    static std::unique_ptr<TimeSlot> create(SlotType type, SlotTime start, SlotTime end, int cap) {
        return std::make_unique<TimeSlot>("SLOT-" + std::to_string(++counter), start, end, cap, type);
    }
};
int SlotFactory::counter = 0;

// โ”€โ”€โ”€ Booking Service โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
class BookingService {
    std::unordered_map<std::string, TimeSlot*> slots;
    std::vector<std::unique_ptr<TimeSlot>> slotStorage;
    std::unordered_map<std::string, std::unique_ptr<Booking>> bookings;
    std::unordered_map<std::string, std::vector<Booking*>> customerBookings;
    std::mutex mtx;

public:
    void addSlot(std::unique_ptr<TimeSlot> slot) {
        TimeSlot* ptr = slot.get();
        slots[ptr->id] = ptr;
        slotStorage.push_back(std::move(slot));
    }

    Booking* bookSlot(Customer* customer, const std::string& slotId) {
        std::lock_guard<std::mutex> lock(mtx);
        auto it = slots.find(slotId);
        if (it == slots.end()) throw std::runtime_error("Slot not found: " + slotId);
        TimeSlot* slot = it->second;

        if (hasConflict(customer, slot))
            throw std::runtime_error(customer->name + " already has overlapping booking");

        std::unique_ptr<Booking> booking;
        if (slot->isAvailable()) {
            slot->book();
            booking = std::make_unique<Booking>(customer, slot, BookingStatus::CONFIRMED);
            std::cout << "  [NOTIFY] Confirmed for " << customer->name << "\n";
        } else {
            slot->addToWaitlist(customer);
            booking = std::make_unique<Booking>(customer, slot, BookingStatus::WAITLISTED);
            std::cout << "  [NOTIFY] " << customer->name << " waitlisted (pos: "
                      << slot->waitlist.size() << ")\n";
        }

        Booking* ptr = booking.get();
        customerBookings[customer->id].push_back(ptr);
        bookings[ptr->id] = std::move(booking);
        return ptr;
    }

    void cancelBooking(const std::string& bookingId) {
        std::lock_guard<std::mutex> lock(mtx);
        auto it = bookings.find(bookingId);
        if (it == bookings.end()) throw std::runtime_error("Booking not found");
        Booking* booking = it->second.get();

        BookingStatus prev = booking->status;
        booking->status = BookingStatus::CANCELLED;
        std::cout << "  [NOTIFY] Cancelled for " << booking->customer->name << "\n";

        if (prev == BookingStatus::CONFIRMED) {
            TimeSlot* slot = booking->slot;
            slot->release();

            Customer* promoted = slot->promoteFromWaitlist();
            if (promoted) {
                slot->book();
                Booking* pb = findWaitlisted(promoted, slot);
                if (pb) {
                    pb->status = BookingStatus::CONFIRMED;
                    std::cout << "  [NOTIFY] " << promoted->name << " promoted from waitlist!\n";
                }
            }
        }
    }

    void displaySlots() {
        std::cout << "\n--- All Delivery Slots ---\n";
        for (auto& s : slotStorage) std::cout << "  " << s->toString() << "\n";
    }

private:
    bool hasConflict(Customer* customer, TimeSlot* target) {
        auto it = customerBookings.find(customer->id);
        if (it == customerBookings.end()) return false;
        for (auto* b : it->second) {
            if (b->status == BookingStatus::CONFIRMED && b->slot->overlaps(*target))
                return true;
        }
        return false;
    }

    Booking* findWaitlisted(Customer* customer, TimeSlot* slot) {
        auto it = customerBookings.find(customer->id);
        if (it == customerBookings.end()) return nullptr;
        for (auto* b : it->second) {
            if (b->slot == slot && b->status == BookingStatus::WAITLISTED)
                return b;
        }
        return nullptr;
    }
};

// โ”€โ”€โ”€ Main Demo โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
int main() {
    std::cout << "=======================================\n";
    std::cout << "   DELIVERY SLOT BOOKING - LLD DEMO   \n";
    std::cout << "=======================================\n\n";

    BookingService service;

    std::cout << "--- Creating Delivery Slots ---\n";
    service.addSlot(SlotFactory::create(SlotType::STANDARD, {9,0}, {11,0}, 3));
    service.addSlot(SlotFactory::create(SlotType::EXPRESS, {11,0}, {13,0}, 2));
    service.addSlot(SlotFactory::create(SlotType::STANDARD, {14,0}, {16,0}, 3));
    service.addSlot(SlotFactory::create(SlotType::SAME_DAY, {18,0}, {21,0}, 2));
    service.displaySlots();

    Customer alice{"C1", "Alice", "123 Main St"};
    Customer bob{"C2", "Bob", "456 Oak Ave"};
    Customer charlie{"C3", "Charlie", "789 Pine Rd"};
    Customer diana{"C4", "Diana", "321 Elm St"};
    Customer eve{"C5", "Eve", "654 Maple Dr"};

    std::cout << "\n--- Booking Slots ---\n";
    auto* b1 = service.bookSlot(&alice, "SLOT-1");
    std::cout << "  " << b1->toString() << "\n";
    auto* b2 = service.bookSlot(&bob, "SLOT-1");
    std::cout << "  " << b2->toString() << "\n";
    auto* b3 = service.bookSlot(&charlie, "SLOT-1");
    std::cout << "  " << b3->toString() << "\n";

    std::cout << "\n--- Morning Full - Waitlist ---\n";
    auto* b4 = service.bookSlot(&diana, "SLOT-1");
    std::cout << "  " << b4->toString() << "\n";
    auto* b5 = service.bookSlot(&eve, "SLOT-1");
    std::cout << "  " << b5->toString() << "\n";

    service.displaySlots();

    std::cout << "\n--- Cancel Alice (promote Diana) ---\n";
    service.cancelBooking(b1->id);
    std::cout << "  Alice: " << bookingStatusStr(b1->status) << "\n";
    std::cout << "  Diana: " << bookingStatusStr(b4->status) << "\n";

    service.displaySlots();

    std::cout << "\n--- Conflict Detection ---\n";
    try {
        service.addSlot(SlotFactory::create(SlotType::EXPRESS, {10,0}, {12,0}, 5));
        service.bookSlot(&bob, "SLOT-5");
    } catch (const std::runtime_error& e) {
        std::cout << "  Expected: " << e.what() << "\n";
    }

    std::cout << "\n--- Express Slot Booking ---\n";
    auto* b6 = service.bookSlot(&alice, "SLOT-2");
    std::cout << "  " << b6->toString() << "\n";
    auto* b7 = service.bookSlot(&charlie, "SLOT-2");
    std::cout << "  " << b7->toString() << "\n";
    auto* b8 = service.bookSlot(&eve, "SLOT-2");
    std::cout << "  " << b8->toString() << "\n";

    std::cout << "\n--- Cancel Charlie from Express (promote Eve) ---\n";
    service.cancelBooking(b7->id);
    service.displaySlots();

    std::cout << "\n=======================================\n";
    std::cout << "           DEMO COMPLETE               \n";
    std::cout << "=======================================\n";
    return 0;
}

State Transitions

stateDiagram-v2
    [*] --> CONFIRMED : capacity available
    [*] --> WAITLISTED : slot full
    CONFIRMED --> CANCELLED : customer cancels
    WAITLISTED --> CONFIRMED : promoted from waitlist
    WAITLISTED --> CANCELLED : customer cancels
    CANCELLED --> [*]
    CONFIRMED --> [*]

Sequence Diagram - Book and Promote Flow

sequenceDiagram
    participant C1 as Customer1
    participant C2 as Customer2
    participant BS as BookingService
    participant Slot as TimeSlot
    participant Obs as Observer

    C1->>BS: bookSlot(slotId)
    BS->>Slot: isAvailable()
    Slot-->>BS: false (full)
    BS->>Slot: addToWaitlist(C1)
    BS->>Obs: onWaitlisted(booking)
    BS-->>C1: Booking (WAITLISTED)

    Note over BS: Another customer cancels
    C2->>BS: cancelBooking(bookingId)
    BS->>Slot: release()
    BS->>Slot: promoteFromWaitlist()
    Slot-->>BS: Customer1
    BS->>Slot: book()
    BS->>Obs: onCustomerPromoted(booking)
    Obs-->>C1: notification sent

How to Extend

Extension Implementation
Recurring slots Template for daily/weekly slot generation
Priority waitlist Replace FIFO queue with priority queue (premium customers first)
Delivery zones Add zone to TimeSlot; filter by customerโ€™s zone
Dynamic capacity Adjust capacity based on driver availability / weather
Slot pricing Express costs more; surge pricing during high demand
Notification channels Multiple observers: SMS, email, push notification
Slot locking Temporary hold (5 min) during checkout to prevent race

What Interviewers Look For

  1. โœ… Capacity management - atomic book/release with thread-safety
  2. โœ… Waitlist FIFO - fair auto-promotion on cancellation
  3. โœ… Conflict detection - overlapping slot check per customer
  4. โœ… Factory pattern - slot creation with type-specific defaults
  5. โœ… Observer pattern - decoupled notifications
  6. โœ… Template method - standardized booking flow
  7. โœ… Clean state management - booking status transitions
  8. โœ… Concurrency - locks prevent overbooking


Scale this design past a single process and these are the concepts it runs into:

Discussion

Newest first
You

Free system design + DSA prep. If it helped you crack an interview, consider supporting.

SensAI SensAI
Beta
Listening...
Tap mic to stop voice mode

Shape what we build next

Every piece of feedback is read by the team and directly influences our roadmap.

What type of feedback?

Install SystemCraft

Add to your home screen for instant access, offline reading, and a distraction-free experience.

Offline reading Faster loads No browser tabs App-like feel

Unlock AI Features

One click to activate - no payment, no credit card. Just sign in and you're in.

AI code review and hints
SensAI chat assistant
AI mock interviews
Whiteboard analysis
100% free during early access