Case Studies

Hotel Booking System

Design a hotel booking system — rooms, availability, reservations, check-in / check-out.

3 min readDesigns · LLD & Patterns

Interview practice: try designing this yourself before reading. Go to the exercise ↓

Problem Statement

Design a hotel booking system that:

  • Manages multiple hotels with rooms of various types
  • Handles reservations for date ranges
  • Avoids double-booking
  • Tracks check-in / check-out, computes the bill
  • Supports cancellation and modification

Requirements

Functional

  • Hotels with rooms of types: standard, deluxe, suite
  • Search rooms by location, dates, capacity, price range
  • Reserve room(s) for a date range
  • Check-in (assign physical room) and check-out (compute bill)
  • Cancel / modify reservation
  • Loyalty / corporate rates

Non-Functional

  • No double-booking on any (room, date) pair
  • Concurrent searches and reservations
  • Audit log

Class Diagram


Inventory: Per-Date Availability

The naive approach — scan all rooms — is slow. Better: maintain an availability counter per (hotel, type, date).

public class Inventory {
    // (hotel, type, date) -> count of available rooms
    private final Map<InventoryKey, Integer> available = new ConcurrentHashMap<>();
    // (hotel, type) -> total inventory
    private final Map<HotelType, Integer> total = new HashMap<>();

    public synchronized int countAvailable(String hotelId, RoomType type,
                                            LocalDate checkIn, LocalDate checkOut) {
        int min = Integer.MAX_VALUE;
        for (LocalDate d = checkIn; d.isBefore(checkOut); d = d.plusDays(1)) {
            int avail = available.getOrDefault(
                new InventoryKey(hotelId, type, d),
                total.getOrDefault(new HotelType(hotelId, type), 0));
            min = Math.min(min, avail);
        }
        return min;
    }

    public synchronized boolean reserve(String hotelId, RoomType type,
                                        LocalDate checkIn, LocalDate checkOut) {
        if (countAvailable(hotelId, type, checkIn, checkOut) < 1) return false;
        for (LocalDate d = checkIn; d.isBefore(checkOut); d = d.plusDays(1)) {
            InventoryKey k = new InventoryKey(hotelId, type, d);
            available.merge(k, -1, Integer::sum);
        }
        return true;
    }

    public synchronized void release(String hotelId, RoomType type,
                                     LocalDate checkIn, LocalDate checkOut) {
        for (LocalDate d = checkIn; d.isBefore(checkOut); d = d.plusDays(1)) {
            available.merge(new InventoryKey(hotelId, type, d), 1, Integer::sum);
        }
    }
}

This decouples booking a room type from assigning a specific room. The physical room is assigned at check-in.


Reservation

public class Reservation {
    public final String id;
    public final Guest guest;
    public final String hotelId;
    public final RoomType roomType;
    public final LocalDate checkIn;
    public final LocalDate checkOut;
    public final int numGuests;
    private ReservationStatus status = ReservationStatus.CONFIRMED;
    private Room assignedRoom;     // null until check-in
    private Bill bill;

    public Reservation(Guest g, String hotelId, RoomType type,
                       LocalDate in, LocalDate out, int guests) {
        if (!out.isAfter(in)) throw new IllegalArgumentException("checkOut must be after checkIn");
        this.id = UUID.randomUUID().toString();
        this.guest = g; this.hotelId = hotelId;
        this.roomType = type;
        this.checkIn = in; this.checkOut = out;
        this.numGuests = guests;
    }

    public long nights() { return ChronoUnit.DAYS.between(checkIn, checkOut); }

    public void assignRoom(Room r) { this.assignedRoom = r; }

    public synchronized void transitionTo(ReservationStatus next) {
        if (!isValidTransition(status, next))
            throw new IllegalStateException();
        status = next;
    }
}

Pricing (Strategy)

public interface PricingStrategy {
    Money totalFor(Reservation r);
}

public class StandardPricing implements PricingStrategy {
    private final TaxCalculator tax;

    @Override
    public Money totalFor(Reservation r) {
        Room room = r.getAssignedRoom();
        Money base = room == null
            ? defaultRateFor(r.roomType)
            : room.getBasePricePerNight();
        Money subtotal = base.times(r.nights());
        Money loyalty = applyLoyaltyDiscount(subtotal, r.getGuest());
        Money taxAmount = tax.calculate(subtotal.minus(loyalty), r.hotelId);
        return subtotal.minus(loyalty).plus(taxAmount);
    }
}

public class WeekendPricing implements PricingStrategy {
    private final PricingStrategy weekday;
    private final double weekendMultiplier;

    @Override
    public Money totalFor(Reservation r) {
        long weekendNights = countWeekendNights(r.checkIn, r.checkOut);
        long weekdayNights = r.nights() - weekendNights;
        Money base = defaultRateFor(r.roomType);
        return base.times(weekdayNights)
            .plus(base.times(weekendNights).times((long) weekendMultiplier));
    }
}

HotelService (Facade)

public class HotelService {
    private final Inventory inventory;
    private final PricingStrategy pricing;
    private final RoomAssigner roomAssigner;

    public List<HotelOption> search(SearchCriteria c) {
        return hotels.stream()
            .filter(h -> matchesLocation(h, c.location))
            .flatMap(h -> Arrays.stream(RoomType.values())
                .filter(t -> inventory.countAvailable(h.getId(), t, c.checkIn, c.checkOut) >= c.rooms)
                .map(t -> new HotelOption(h, t, pricing.totalFor(stub(h, t, c)))))
            .toList();
    }

    public Reservation book(Guest guest, String hotelId, RoomType type,
                             LocalDate checkIn, LocalDate checkOut, int numGuests) {
        synchronized (this) {
            if (!inventory.reserve(hotelId, type, checkIn, checkOut)) {
                throw new NoAvailabilityException();
            }
        }
        return new Reservation(guest, hotelId, type, checkIn, checkOut, numGuests);
    }

    public void cancel(Reservation r) {
        if (r.getStatus() != ReservationStatus.CONFIRMED) {
            throw new IllegalStateException("can only cancel confirmed");
        }
        r.transitionTo(ReservationStatus.CANCELLED);
        inventory.release(r.hotelId, r.roomType, r.checkIn, r.checkOut);
        // Refund per cancellation policy
    }

    public Room checkIn(Reservation r) {
        if (!r.checkIn.equals(LocalDate.now()))
            throw new IllegalStateException("not check-in date");
        Room room = roomAssigner.assignSpecificRoom(r.hotelId, r.roomType);
        r.assignRoom(room);
        room.transitionTo(RoomStatus.OCCUPIED);
        r.transitionTo(ReservationStatus.CHECKED_IN);
        return room;
    }

    public Bill checkOut(Reservation r) {
        Money total = pricing.totalFor(r);
        Bill bill = new Bill(total, r);
        r.assignedRoom.transitionTo(RoomStatus.AVAILABLE);
        r.transitionTo(ReservationStatus.CHECKED_OUT);
        return bill;
    }
}

Sequence: Search → Book → Check-in → Check-out


Edge Cases

CaseHandling
Walk-in (no reservation)Direct check-in; create reservation on the fly
Early check-outCharge per cancellation policy; partial refund
Late check-outExtra hourly fee or extend a night
Room maintenance during reservationReassign at check-in to another room of the same type
Overbooking (intentional)Walk to a sister hotel + comp
No-showCharge first-night fee; cancel reservation
Multi-room booking (groups)Reserve N rooms in one transaction; all-or-nothing

Design Patterns Used

PatternWhere
StateReservation lifecycle, Room status
StrategyPricing (standard, weekend, corporate, dynamic)
FacadeHotelService
RepositoryHotels, Reservations, Guests
ObserverNotifications (confirmation, check-in reminder)
SingletonOne service per app

Interview Tips

  • Distinguish type-level reservation (counter) from room-level assignment (at check-in). This separation simplifies overbooking and maintenance handling.
  • Per-date counter beats per-room interval scan for search and concurrency.
  • Mention overbooking as a deliberate strategy in real hotels (most hotels overbook by ~5%).
  • Pricing strategy should compose: base × nights → loyalty discount → tax.

Practice

Try it yourself

Design the classes for a hotel booking system.

Your design should cover

  • Rooms of different types and prices
  • Search availability for a date range
  • Book, cancel, check in and check out
  • Never double-book a room for overlapping dates
  • Pricing that varies by season or demand
0 / 8,000 characters · saved in this browser

Finished reading?

Mark it done to track your progress in Designs.