Home/Learn/Low Level Design/Design a Hotel Management System

Design a Hotel Management System

Intermediate
LLD Interview Problems
Java Source Code

Model a hotel that manages room types, bookings, guest check-in/check-out, and invoice generation using Strategy and Observer patterns.

Overview

A Hotel Management System tracks rooms across multiple types (SINGLE, DOUBLE, SUITE) and statuses (AVAILABLE, BOOKED, OCCUPIED, MAINTENANCE). Guests hold a profile; a Booking links a Guest to a Room with check-in and check-out dates. PricingStrategy allows seasonal or room-type-based rate switching without touching Booking logic. HousekeepingObserver is notified on guest check-out so staff can schedule cleaning. An InvoiceService computes charges from the Booking duration and the active pricing strategy. Factory creates the correct Room subtype from configuration, keeping the hotel setup flexible.

Requirements Analysis

Functional: search available rooms by type and date range, book a room, check in a guest, check out a guest and generate invoice, assign housekeeping tasks. Non-functional: pricing rules swappable without modifying Booking, housekeeping notifications decoupled from checkout flow, room status transitions safe under concurrent access.

Requirements
// Entities : Hotel, Room, RoomStatus, RoomType, Guest, Booking, Invoice, HousekeepingTask
// Patterns : Strategy (pricing), Observer (housekeeping), Factory (room creation)

Core Classes & Relationships

RoomType enum: SINGLE, DOUBLE, SUITE. RoomStatus enum: AVAILABLE, BOOKED, OCCUPIED, MAINTENANCE. PricingStrategy interface with calculateRate(RoomType, long nights). HousekeepingObserver interface fires on guest checkout. Room is abstract; SingleRoom, DoubleRoom, SuiteRoom extend it. Booking links Guest, Room, dates, and holds the resolved price.

Java — enums & interfaces
public enum RoomType   { SINGLE, DOUBLE, SUITE }
public enum RoomStatus { AVAILABLE, BOOKED, OCCUPIED, MAINTENANCE }

public interface PricingStrategy {
    double calculateRate(RoomType type, long nights);
}

public class StandardPricing implements PricingStrategy {
    @Override public double calculateRate(RoomType type, long nights) {
        double base = switch (type) {
            case SINGLE -> 2000.0;
            case DOUBLE -> 3500.0;
            case SUITE  -> 8000.0;
        };
        return base * nights;
    }
}

public class SeasonalPricing implements PricingStrategy {
    private final double surgeMultiplier;
    public SeasonalPricing(double surgeMultiplier) { this.surgeMultiplier = surgeMultiplier; }
    @Override public double calculateRate(RoomType type, long nights) {
        return new StandardPricing().calculateRate(type, nights) * surgeMultiplier;
    }
}

public interface HousekeepingObserver {
    void onCheckout(String roomNumber);
}

Java Implementation

Room stores its number, type, and status. Booking records guest, room, dates and total charge. Hotel.bookRoom() finds the first available matching room, creates a Booking, and sets room status to BOOKED. Hotel.checkIn() transitions to OCCUPIED. Hotel.checkOut() computes the invoice via PricingStrategy, notifies HousekeepingObservers, and marks the room AVAILABLE.

Java — core classes
public class Room {
    private final String number;
    private final RoomType type;
    private RoomStatus status = RoomStatus.AVAILABLE;

    public Room(String number, RoomType type) { this.number = number; this.type = type; }
    public String getNumber()           { return number; }
    public RoomType getType()           { return type; }
    public RoomStatus getStatus()       { return status; }
    public void setStatus(RoomStatus s) { this.status = s; }
    public boolean isAvailable()        { return status == RoomStatus.AVAILABLE; }
}

public class Guest {
    private final String guestId;
    private final String name;
    public Guest(String guestId, String name) { this.guestId = guestId; this.name = name; }
    public String getGuestId() { return guestId; }
    public String getName()    { return name; }
}

public class Booking {
    private final String bookingId;
    private final Guest guest;
    private final Room room;
    private final LocalDate checkIn;
    private final LocalDate checkOut;
    private double totalCharge;

    public Booking(String bookingId, Guest guest, Room room, LocalDate checkIn, LocalDate checkOut) {
        this.bookingId = bookingId; this.guest = guest; this.room = room;
        this.checkIn = checkIn; this.checkOut = checkOut;
    }
    public long nights()           { return ChronoUnit.DAYS.between(checkIn, checkOut); }
    public void setTotalCharge(double c) { this.totalCharge = c; }
    public double getTotalCharge() { return totalCharge; }
    public Room getRoom()          { return room; }
    public Guest getGuest()        { return guest; }
}

public class Hotel {
    private final List<Room> rooms = new ArrayList<>();
    private final List<Booking> bookings = new ArrayList<>();
    private final PricingStrategy pricing;
    private final List<HousekeepingObserver> housekeepers = new ArrayList<>();
    private int bookingCounter = 0;

    public Hotel(PricingStrategy pricing) { this.pricing = pricing; }
    public void addRoom(Room room)                      { rooms.add(room); }
    public void addHousekeeper(HousekeepingObserver h)  { housekeepers.add(h); }

    public Booking bookRoom(Guest guest, RoomType type, LocalDate checkIn, LocalDate checkOut) {
        Room room = rooms.stream()
            .filter(r -> r.getType() == type && r.isAvailable())
            .findFirst()
            .orElseThrow(() -> new IllegalStateException("No available " + type + " room"));
        room.setStatus(RoomStatus.BOOKED);
        Booking booking = new Booking("BK-" + (++bookingCounter), guest, room, checkIn, checkOut);
        bookings.add(booking);
        return booking;
    }

    public void checkIn(Booking booking) {
        booking.getRoom().setStatus(RoomStatus.OCCUPIED);
        System.out.println(booking.getGuest().getName() + " checked into room " + booking.getRoom().getNumber());
    }

    public double checkOut(Booking booking) {
        long nights = booking.nights();
        double charge = pricing.calculateRate(booking.getRoom().getType(), nights);
        booking.setTotalCharge(charge);
        booking.getRoom().setStatus(RoomStatus.AVAILABLE);
        housekeepers.forEach(h -> h.onCheckout(booking.getRoom().getNumber()));
        System.out.printf("Invoice for %s: %.2f for %d nights%n", booking.getGuest().getName(), charge, nights);
        return charge;
    }
}

Key Points to Remember

  • 1RoomStatus transitions (AVAILABLE→BOOKED→OCCUPIED→AVAILABLE) must be atomic under concurrent booking requests.
  • 2Strategy for pricing means switching from flat to seasonal rates requires only changing the injected PricingStrategy — zero changes to Hotel or Booking.
  • 3Observer for housekeeping decouples checkout from cleaning workflows — new notification channels (SMS, app) are new observer implementations.
  • 4Separating Book (metadata) from Room (physical asset with status) mirrors the Library BookItem pattern and prevents status confusion.

Interview Questions

Sign in to ask Aria
1

How would you handle concurrent bookings to prevent two guests from booking the same room on overlapping dates?

HardAmazon
2

How would you extend the system to support room upgrades and early check-in fees?

MediumUber
3

How would you design the Booking cancellation flow with refund policy rules?

MediumMicrosoft

Ask Aria about Design a Hotel Management System

Your personal AI tutor — ask anything about this concept

Revision Status

Personal Notes

Sign in to save personal notes for this topic.

Discussion

Sign in to join the discussion.

Loading discussion…