Design a Hotel Management System
Rooms, rates, reservations, check-in/out, and housekeeping — a broad domain-modelling exercise.
Problem Statement
Design the object model for a hotel management system that can search available rooms over a requested stay, create reservations, move them through check-in and check-out, price the stay, and produce an invoice.
The heart of the problem is not CRUD. It is the date-range invariant: a room is unavailable for a request if any active reservation for that room overlaps the requested [check-in, check-out) interval. The design should make that rule explicit and reusable across search, booking, cancellation, and billing.
Business context
Hotel Management appears in Amazon, Airbnb, and Oracle style interviews because it combines booking, inventory, state transitions, and pricing in one familiar domain. A good answer separates room inventory from reservation lifecycle, keeps date logic deterministic, and gives the business seams for taxes, seasonal rates, housekeeping, channel managers, and no-show handling.
Interviewers are looking for whether the candidate can protect a scarce resource across time. A design that only checks whether a room is currently empty misses the real problem: future reservations must block overlapping future stays too.
Functional Requirements
Maintain hotel rooms with room number, floor, type, capacity, and operational status.
Support room types such as Standard, Deluxe, and Suite with base nightly rates and guest capacity.
Search available rooms for a room type, guest count, and check-in/check-out date range.
Create a reservation only when the chosen room has no active overlapping reservation.
Move a reservation through Reserved, CheckedIn, CheckedOut, Cancelled, and NoShow states with valid transitions only.
Allow cancellation and no-show marking before check-in so blocked inventory can be released.
Generate an invoice on check-out using a pluggable pricing strategy and optional extra charges.
Notify observers such as housekeeping and availability boards when room status changes.
Non-Functional Requirements
Date-range correctness
Availability must use half-open intervals and the overlap rule requestStart < existingEnd && requestEnd > existingStart. Adjacent stays where one check-out equals the next check-in are allowed.
Lifecycle safety
Reservation transitions must be centralized so a cancelled reservation cannot check in and a checked-out reservation cannot be billed twice through the same flow.
Extensibility
New pricing plans, room creation rules, and observers should be additive classes rather than edits inside the reservation flow.
Low-latency search
The interview implementation can scan in memory, but it must make the hot path clear enough to replace with indexed inventory later.
Auditability
Invoices should capture the reservation id, room charge, extras, tax, total, and issue time so customer disputes can be explained.
Requirement Clarification
QAre check-in and check-out dates inclusive?
Use a half-open interval: check-in is inclusive and check-out is exclusive. A stay from Aug 1 to Aug 4 occupies the nights of Aug 1, Aug 2, and Aug 3, and another guest may check in on Aug 4.
QShould cancelled and no-show reservations block availability?
No. They remain in history but stop blocking inventory. Only Reserved and CheckedIn reservations block a room for overlap checks.
QDo we allocate a specific room at reservation time?
Yes for the base design. The service searches matching rooms and binds the first available room to the reservation. A later extension can support room-type-only inventory pools.
QDo we need payment gateway integration?
No for the core LLD. The system produces an invoice. Payment collection is a boundary adapter that can consume the invoice total.
QHow is housekeeping represented?
Room status changes are observable. Check-out moves the room to Cleaning, an observer can dispatch housekeeping, and the room becomes searchable again only after it is marked Available.
UML Class Diagram
Sequence Diagram
Entity Identification
Room
Represents a physical room. It owns operational status, knows its room type, and notifies observers whenever status changes.
RoomType
Defines capacity and base nightly rate for Standard, Deluxe, and Suite rooms. Search uses capacity, pricing uses the rate.
Reservation
Binds a guest, room, date range, guest count, and current state. It owns the overlap predicate and delegates valid transitions to its state object.
ReservationState
State pattern seam for reservation lifecycle. Concrete states decide whether inventory is blocked and which transitions are legal.
SearchService
Filters candidate rooms by type, guest capacity, room status, and active overlapping reservations using one date-range rule.
PricingStrategy
Calculates invoice totals from nights, room type rate, extra charges, and tax. Seasonal or channel-specific pricing can replace the default strategy.
Invoice
Immutable billing result produced at check-out with room charge, extras, tax, total, reservation id, and issue timestamp.
HotelService
Application service and aggregate boundary. It owns collections, creates rooms and reservations, coordinates lifecycle calls, and returns invoices.
RoomObserver
Observer interface for secondary reactions to room status changes, such as dispatching housekeeping or updating an availability board.
Design Patterns Used
ReservationState keeps lifecycle rules out of service code. Reserved permits check-in, cancellation, and no-show; CheckedIn permits check-out; terminal states reject further transitions.
PricingStrategy isolates rate calculation and invoice creation. The check-out flow does not know whether pricing is standard, seasonal, member-discounted, or channel-specific.
RoomFactory centralizes room creation so validation, default status, room-number policy, and future room subclasses are not scattered across service methods.
RoomObserver lets housekeeping and availability boards react to room status changes without coupling those concerns to Room or HotelService.
Step-by-Step Design
1Define the date-range invariant first
Use half-open stays and one overlap predicate everywhere. This allows back-to-back reservations and prevents the classic bug where checking current room status ignores future bookings.
public boolean overlaps(LocalDate start, LocalDate end) { validateDateRange(start, end); return blocksInventory() && start.isBefore(checkOutDate) && end.isAfter(checkInDate); }2Separate room type from room status
RoomType captures stable commercial facts like rate and capacity. RoomStatus captures operational facts like Available, Occupied, Cleaning, and Maintenance.
3Put lifecycle behavior behind state objects
The reservation exposes checkIn, checkOut, cancel, and markNoShow, but the current state decides what each call means. Illegal transitions fail immediately.
final class ReservedState implements ReservationState { public void checkIn(Reservation reservation) { reservation.transitionTo(new CheckedInState()); reservation.getRoom().markOccupied(); } }4Search by filtering rooms against active reservations
SearchService first filters by room type, capacity, and operational status. It then rejects any room with an active reservation whose date range overlaps the request.
5Generate invoices through a strategy
Check-out transitions the reservation, moves the room to Cleaning, and asks the injected pricing strategy for the invoice. The service coordinates; it does not hard-code rate math.
public Invoice checkOut(String reservationId, List<Charge> extras) { Reservation reservation = getReservation(reservationId); reservation.checkOut(); return pricingStrategy.createInvoice(reservation, extras); }6Notify housekeeping and availability as observers
Room status changes are local events. Observers subscribe to rooms and update secondary views without the domain model knowing those consumers exist.
Complete Java Implementation
Explanation of Every Class
Room
Physical inventory unit. It stores room number, type, floor, and operational status, and fires observer callbacks when status changes. Search rejects rooms that are Cleaning or under Maintenance.
RoomType
Enum for stable commercial configuration: base nightly rate and maximum guest capacity. This avoids scattering rates and capacity checks across search and billing.
Reservation
Reservation owns the date range, guest binding, selected room, and state. Its overlaps method is the single source of truth for date-range availability.
PricingStrategy
Pricing seam plus the default implementation and invoice data model. StandardPricingStrategy multiplies nights by room rate, adds extras, applies tax, and returns an immutable Invoice.
SearchService
Availability query service. It filters by room type, capacity, status, and active overlapping reservations. It also contains sample observers for housekeeping and availability counts.
HotelService
Application boundary. It stores rooms and reservations, uses the factory to add rooms, delegates search to SearchService, delegates billing to PricingStrategy, and triggers reservation lifecycle methods.
Main
Small executable scenario showing search, reservation creation, overlap behavior, check-in, check-out, invoice generation, and housekeeping-driven availability restoration.
Dry Run
Sample input
Rooms: 201 Deluxe, 202 Deluxe. Existing none. Guest asks for Deluxe, 2 guests, Aug 1 to Aug 4. Then another guest searches Aug 3 to Aug 5 and Aug 4 to Aug 6.
| Step | Request | Overlap check | Room status | Reservation state | Outcome |
|---|---|---|---|---|---|
| 1 | Search Deluxe Aug 1 to Aug 4 | No active reservations | 201 Available, 202 Available | None | Both rooms returned |
| 2 | Reserve Ava in 201 Aug 1 to Aug 4 | No overlap, create R1 | 201 Available | R1 Reserved | 201 is blocked for overlapping future stays |
| 3 | Search Deluxe Aug 3 to Aug 5 | Aug 3 < Aug 4 and Aug 5 > Aug 1 | 201 Available, 202 Available | R1 Reserved | 201 rejected, 202 returned |
| 4 | Search Deluxe Aug 4 to Aug 6 | Start equals R1 check-out, so no overlap | 201 Available, 202 Available | R1 Reserved | 201 and 202 returned |
| 5 | Check in and check out R1 | Historical reservation no longer blocks | 201 Cleaning | R1 CheckedOut | Invoice = 3 nights plus extras plus tax |
| 6 | Housekeeping marks 201 available | No active overlap | 201 Available | R1 CheckedOut | 201 can appear in future search again |
The important row is step 3: even though room 201 is operationally Available before check-in, the future reservation blocks an overlapping request. Step 4 proves adjacent stays are legal under half-open intervals.
Complexity Analysis
| Operation | Time | Space | Note |
|---|---|---|---|
| search available rooms | O(R × B) | O(R) | R rooms and B reservations in the in-memory implementation. Each room may scan reservations for overlap. |
| reserve | O(R × B) | O(1) | Delegates to search, then creates one reservation and stores it by id. |
| checkIn / cancel / markNoShow | O(1) | O(1) | Map lookup plus a state transition; observer notifications are proportional to subscribers on room status changes. |
| checkOut and invoice | O(E + O) | O(E) | E extra charges and O room observers. Night count is constant-time date arithmetic. |
| single overlap check | O(1) | O(1) | Two date comparisons after validating the range. |
The scan is acceptable for an interview model. Production search would index by room type and date, or precompute nightly inventory buckets, but the same half-open overlap rule remains the correctness contract.
Extensibility
Seasonal and channel pricing
Add another PricingStrategy that reads rate calendars, promo codes, loyalty tiers, or OTA channel markups. HotelService.checkOut does not change.
Room features
Add amenities such as ocean view, smoking preference, accessibility, or connecting rooms as filters on Room or as a separate RoomFeature set used by SearchService.
Room-type inventory pools
Instead of binding a physical room at booking time, reserve capacity at room-type level and assign a room on check-in. This improves operations but shifts overlap checks to inventory counters.
External integrations
Observers or adapters can publish availability updates to channel managers, send housekeeping tasks, or push invoices to accounting without changing the domain classes.
Alternative Designs
Nightly inventory buckets
Track remaining count per room type per night, decrementing all nights in the requested range when a reservation is created.
Tradeoffs
Search becomes fast for type-level availability, but assigning a specific room later needs a second placement step and careful rollback across multiple nights.
Repository and specification pattern
Move room and reservation lookup into repositories, and express filters as specifications such as RoomTypeMatches and NoOverlapForRange.
Tradeoffs
Better for persistence-heavy systems, but verbose for a machine-coding interview unless the interviewer asks for database boundaries.
Workflow engine for reservation lifecycle
Represent lifecycle transitions in a configurable workflow table instead of state classes.
Tradeoffs
Useful for enterprise policy changes, but the State pattern is clearer and safer for an interview-sized domain model.
Common Mistakes
- ×
Checking only RoomStatus.Available and forgetting future active reservations, which allows double booking.
- ×
Using inclusive end dates so a guest checking out on Aug 4 blocks another guest checking in on Aug 4.
- ×
Letting Cancelled or NoShow reservations continue to block search results.
- ×
Putting lifecycle transitions in nested service conditionals instead of a State pattern, making illegal transitions easy to miss.
- ×
Hard-coding room rates and tax in HotelService rather than a pricing strategy.
- ×
Marking a room Available immediately on check-out and skipping the Cleaning state and housekeeping workflow.
- ×
Creating an invoice before the reservation successfully transitions to CheckedOut.
Follow-up Interview Questions
QHow would you prevent two users from reserving the last room concurrently?
Wrap search plus reservation creation in a transaction or a lock on the room/date inventory. In a database design, use a uniqueness constraint or row locks over room and date-range inventory rows.
QHow would you support modifying reservation dates?
Validate the new range with the same overlap predicate while excluding the reservation being modified. Apply the change only if the new range has no active conflict.
QHow do you price weekend or holiday rates?
Implement a pricing strategy that iterates each night, looks up a rate calendar for that date and room type, then adds extras and tax before returning an invoice.
QWhere should payment processing live?
Outside the core model. The hotel domain creates invoices; a payment adapter charges the guest and records payment status against the invoice or reservation.
QHow would you support overbooking?
Introduce an inventory policy per room type that allows reserved count to exceed physical count by a configured buffer. Keep this out of Reservation.overlaps and put it in a higher-level allocation policy.
Production Considerations
Persistence and transactions
Store rooms, reservations, invoices, and state transitions durably. Reservation creation should be transactional with conflict detection to avoid double booking under concurrency.
Time zones and hotel calendars
Hotel stays use local property dates, not arbitrary instants. Keep property time zone and local check-in/check-out rules explicit.
Audit trail
Record who changed a reservation, previous state, next state, invoice inputs, and pricing strategy version. Hospitality disputes often depend on historical policy.
Operational reliability
Housekeeping, channel manager sync, email, and accounting should be asynchronous with retry and idempotency because those integrations fail independently of booking.
Search performance
Large hotels or multi-property systems need indexes by property, room type, and date range. The in-memory scan becomes an indexed availability query or cached inventory calendar.
What Interviewers Look For
Did the candidate state half-open date ranges and implement the correct overlap condition?
Can they explain which reservation states block inventory and which do not?
Is pricing behind an interface rather than mixed into check-out orchestration?
Do observers represent side effects without coupling housekeeping to the room entity?
Can the design evolve from a single hotel in memory to persisted, concurrent, multi-property inventory?
Quiz
0/5 answered
1.Which condition correctly detects overlap for half-open hotel stays?
2.Which reservation states should normally block inventory?
3.Why is pricing modeled as **PricingStrategy**?
4.What should happen when a checked-in guest checks out?
5.What does the Observer pattern decouple in this design?
Practice Variants
Add rate calendars
IntermediateImplement a pricing strategy that charges different rates per night based on weekday, weekend, holiday, and seasonal demand.
Support reservation modification
IntermediateAllow changing dates, room type, and guest count while preventing overlap with other active reservations for the same room.
Multi-property search
AdvancedAdd Hotel or Property as a top-level aggregate and search availability across city, property, room type, and date range.
Flashcards
Cheat Sheet
Core entities: Room, RoomType, Reservation, ReservationState, SearchService, PricingStrategy, Invoice, HotelService, RoomObserver.
Overlap rule: use half-open stays. A request conflicts when requestStart < existingEnd && requestEnd > existingStart for the same room and an active reservation.
States: Reserved can check in, cancel, or no-show. CheckedIn can check out. CheckedOut, Cancelled, and NoShow are terminal and do not block inventory.
Patterns: State for reservation lifecycle, Strategy for pricing, Factory Method for room creation, Observer for housekeeping and availability updates.
Flow: search rooms → reject overlapping active reservations → create Reserved reservation → check in marks room Occupied → check out marks room Cleaning and creates invoice → housekeeping marks Available.
Complexity: simple search is O(R×B); overlap check is O(1); check-in and cancellation are O(1); check-out is O(extras + observers).
References
- BookHead First Design Patterns (State, Strategy, Observer, Factory) — Freeman & Robson
- BlogMartin Fowler — Temporal Patterns — Martin Fowler
- DocsOracle Java Docs — LocalDate — Oracle