Back to LLD Problems: Parking Lot, Elevator, Chess, etc.
Q4Question 4 of 12

Design a Hotel Booking System

Key Concepts
  • The hardest part is date-range availability: a room is available only if no existing reservation overlaps the requested dates.
  • Overlap is checked with the standard condition: one range starts before the other ends and vice versa.
  • A Hotel owns rooms grouped by RoomType (standard, deluxe, suite), each with a nightly price.
  • A Reservation moves through a lifecycle: CONFIRMED, CHECKED_IN, COMPLETED, or CANCELLED.
  • Cancellation is isolated behind a CancellationPolicy interface so the business can switch between free-until-24h and tiered penalties.
  • Concurrency is critical: two guests booking the last room for overlapping dates must be serialised with an atomic check-and-reserve.
  • This maps directly to reserving vault lockers at National Bank of India for a date range.

Definition

A hotel system reserves a typed room for a guest over a date range and manages the booking lifecycle with a cancellation policy.
  • Hotel – owns rooms grouped by RoomType (standard, deluxe, suite), each with a nightly price.
  • Room – has a room number, type, and a method to check availability against existing reservations.
  • Reservation – holds a room for a guest over a date range, progressing through CONFIRMED, CHECKED_IN, COMPLETED, or CANCELLED.
  • Guest – the person making the reservation.
  • CancellationPolicy – a pluggable interface for free-until-24h, tiered penalties, or no-refund policies.
  • Date-range availability – the central algorithmic challenge, using overlap detection.

Think of booking a hotel room like reserving a bank locker for a date range at National Bank of India. Rajesh reserves a deluxe locker from the 1st to the 5th. While reserved, no one else can take that exact locker for overlapping dates. He checks in, uses it, checks out, and if he cancels early the bank applies a cancellation fee based on how late he cancelled.

Understanding the Concepts

What are the core entities?

The core entities are Hotel, Room, RoomType, Reservation, Guest, and CancellationPolicy.

JAVA
package com.bank.hotel.model;

import java.math.BigDecimal;

public enum RoomType {
    STANDARD(new BigDecimal("3000")),
    DELUXE(new BigDecimal("5000")),
    SUITE(new BigDecimal("10000"));

    private final BigDecimal nightlyRate;

    RoomType(BigDecimal nightlyRate) {
        this.nightlyRate = nightlyRate;
    }

    public BigDecimal getNightlyRate() { return nightlyRate; }
}
JAVA
package com.bank.hotel.model;

import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;

public class Room {
    private final String roomNumber;
    private final RoomType type;
    private final List<Reservation> reservations;

    public Room(String roomNumber, RoomType type) {
        this.roomNumber = roomNumber;
        this.type = type;
        this.reservations = new ArrayList<>();
    }

    public boolean isAvailable(LocalDate checkIn, LocalDate checkOut) {
        return reservations.stream()
                .filter(r -> r.getStatus() != ReservationStatus.CANCELLED)
                .noneMatch(r -> r.overlaps(checkIn, checkOut));
    }

    public void addReservation(Reservation reservation) {
        reservations.add(reservation);
    }

    public String getRoomNumber() { return roomNumber; }
    public RoomType getType() { return type; }
}

How does date-range availability work?

This is the central algorithmic challenge. Two date ranges overlap when the start of one is before the end of the other and vice versa.

JAVA
package com.bank.hotel.model;

import java.time.LocalDate;

public class Reservation {
    private final String reservationId;
    private final Guest guest;
    private final Room room;
    private final LocalDate checkIn;
    private final LocalDate checkOut;
    private ReservationStatus status;

    public Reservation(String reservationId, Guest guest, Room room,
                       LocalDate checkIn, LocalDate checkOut) {
        this.reservationId = reservationId;
        this.guest = guest;
        this.room = room;
        this.checkIn = checkIn;
        this.checkOut = checkOut;
        this.status = ReservationStatus.CONFIRMED;
    }

    public boolean overlaps(LocalDate otherCheckIn,
                            LocalDate otherCheckOut) {
        return checkIn.isBefore(otherCheckOut)
                && otherCheckIn.isBefore(checkOut);
    }

    public ReservationStatus getStatus() { return status; }
    public void setStatus(ReservationStatus status) {
        this.status = status;
    }
    public LocalDate getCheckIn() { return checkIn; }
    public LocalDate getCheckOut() { return checkOut; }
    public String getReservationId() { return reservationId; }
}
JAVA
package com.bank.hotel.model;

public enum ReservationStatus {
    CONFIRMED, CHECKED_IN, COMPLETED, CANCELLED
}

How does search work?

Search filters by location, date range, room type, and price, returning available rooms.

JAVA
package com.bank.hotel.service;

import com.bank.hotel.model.*;
import java.time.LocalDate;
import java.util.List;
import java.util.stream.Collectors;

public class HotelService {
    private final Hotel hotel;

    public HotelService(Hotel hotel) {
        this.hotel = hotel;
    }

    public List<Room> searchAvailable(RoomType type,
                                       LocalDate checkIn,
                                       LocalDate checkOut) {
        return hotel.getRooms().stream()
                .filter(r -> r.getType() == type)
                .filter(r -> r.isAvailable(checkIn, checkOut))
                .collect(Collectors.toList());
    }
}

How does booking work?

Booking creates a Reservation in CONFIRMED status. Check-in moves it to CHECKED_IN. Check-out to COMPLETED.

JAVA
public Reservation book(Guest guest, Room room,
                        LocalDate checkIn, LocalDate checkOut) {
    synchronized (room) {
        if (!room.isAvailable(checkIn, checkOut)) {
            throw new IllegalStateException(
                    "Room " + room.getRoomNumber()
                    + " not available for these dates");
        }
        Reservation res = new Reservation(
                java.util.UUID.randomUUID().toString(),
                guest, room, checkIn, checkOut);
        room.addReservation(res);
        System.out.println("Booked room " + room.getRoomNumber()
                + " for " + guest.getName()
                + " from " + checkIn + " to " + checkOut);
        return res;
    }
}

How does the cancellation policy work?

Cancellation runs through a policy interface so the business can choose different penalty rules.

JAVA
package com.bank.hotel.service;

import java.math.BigDecimal;
import java.time.LocalDate;

public interface CancellationPolicy {
    BigDecimal computePenalty(LocalDate checkIn,
                              LocalDate cancellationDate,
                              BigDecimal totalCost);
}
JAVA
package com.bank.hotel.service;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;

public class FreeUntil24hPolicy implements CancellationPolicy {
    @Override
    public BigDecimal computePenalty(LocalDate checkIn,
                                     LocalDate cancellationDate,
                                     BigDecimal totalCost) {
        long daysUntilCheckIn = ChronoUnit.DAYS.between(
                cancellationDate, checkIn);
        if (daysUntilCheckIn >= 1) {
            return BigDecimal.ZERO; // free cancellation
        }
        return totalCost; // full charge for late cancel
    }
}

How do you handle concurrency?

Two guests booking the last deluxe room at the same instant must be serialised. The book method synchronises on the room object so the availability check and reservation insert happen atomically. In production, this would be a database unique constraint or a row-level lock.

What about the reservation lifecycle?

A reservation progresses through a clear lifecycle: CONFIRMED when booked, CHECKED_IN on arrival, COMPLETED on departure, or CANCELLED. Invalid transitions are rejected.

JAVA
public void checkIn(Reservation res) {
    if (res.getStatus() != ReservationStatus.CONFIRMED) {
        throw new IllegalStateException("Cannot check in: " + res.getStatus());
    }
    res.setStatus(ReservationStatus.CHECKED_IN);
}

public void checkOut(Reservation res) {
    if (res.getStatus() != ReservationStatus.CHECKED_IN) {
        throw new IllegalStateException("Cannot check out: " + res.getStatus());
    }
    res.setStatus(ReservationStatus.COMPLETED);
}

What should a senior engineer know?

  • The overlap condition a.start < b.end && b.start < a.end is the foundation. Getting it wrong causes double-booking.
  • Cancellation policy as a Strategy lets the business change rules without touching booking logic.
  • For high-throughput booking systems, use optimistic locking with version numbers instead of pessimistic synchronisation.
  • Pricing can also be a Strategy: flat rate, seasonal surge, loyalty discount.

What are common interview follow-up questions?

  • How do you prevent double-booking of the same room?
  • How would you add seasonal pricing?
  • How do you handle overbooking (airline style)?
  • How would you scale this to a hotel chain with thousands of properties?
  • What is the time complexity of checking availability?

Visual Overview

DIAGRAM
  Reservation Lifecycle:
  ──────────────────────

  CONFIRMED ──checkIn──→ CHECKED_IN ──checkOut──→ COMPLETED
       │
       └──cancel──→ CANCELLED (apply penalty)
DIAGRAM
  Date-Range Overlap Detection:
  ─────────────────────────────

  Existing:   |───────────|
              checkIn    checkOut

  Request A:       |────|           → overlaps (conflict)
  Request B:                |────|  → no overlap (OK)
  Request C:  |────|                → no overlap (OK)

  Overlap condition: a.start < b.end && b.start < a.end
DIAGRAM
  ┌──────────────────────────────────────┐
  │           HotelService                │
  │  search(type, dates)                  │
  │  book(guest, room, dates)             │
  │  cancel(reservation)                  │
  └──────┬──────────────┬────────────────┘
         │              │
         ▼              ▼
  ┌──────────────┐ ┌────────────────────┐
  │    Room      │ │ CancellationPolicy │
  │ isAvailable()│ │ computePenalty()   │
  └──────────────┘ └────────────────────┘

Key Takeaway

When you design a hotel booking system, the hardest and most important piece is date-range availability. A room is available for a requested check-in and check-out only if no existing non-cancelled reservation overlaps those dates, and you test overlap with the standard condition that one range starts before the other ends and vice versa. Getting this exactly right is what prevents double-booking.

The entities are a Hotel that owns rooms grouped by type such as standard, deluxe, and suite each with a nightly price, a Reservation that holds one room for one guest over a date range, and a Guest. A reservation moves through a clear lifecycle: confirmed when booked, checked-in on arrival, completed on departure, or cancelled.

Cancellation is where business rules change constantly, so isolating it behind a cancellation-policy interface is essential. The bank can offer free cancellation up to twenty-four hours before arrival, or a tiered penalty, and switching between them is a new policy class with zero change to the booking service. This is the Open/Closed Principle and Strategy pattern working together.

The concurrency concern is that two guests trying to book the last deluxe room for overlapping dates at the same instant must be serialised, otherwise both succeed and the hotel is oversold. Enforce this with an atomic check-and-reserve, in practice a database constraint or a lock scoped to the room and date range. At National Bank of India, this is identical to reserving a limited physical asset like a vault locker for a date range.

Remember one sentence: a hotel booking system checks date-range overlap atomically, manages a reservation lifecycle, and applies cancellation penalties through a pluggable policy.

---

Share
Use ← → arrow keys to navigate