When an interviewer asks you to design a hotel reservation system, they want to see how you balance business rules with engineering constraints. The conversation usually starts with a high‑level overview, then dives into data modeling, API design, and the hardest parts – like preventing double‑booking under heavy traffic. Below is a practical walkthrough you can follow in real time.

1. Clarify the Scope and Requirements

Begin by asking clarifying questions. This shows you care about the product and avoids building a solution that is too big or too small.

  • Functional
    • Search for rooms by date range, location, and filters (price, amenities).
    • View room details (photos, description, policies).
    • Create, modify, and cancel a reservation.
    • Support multiple room types per reservation (e.g., two king beds + one twin).
    • Handle payments and refunds (you can abstract the payment provider).
  • Non‑functional
    • Availability: System must prevent double‑booking.
    • Latency: Search queries should return within a few hundred milliseconds.
    • Scalability: Should handle peak traffic during holiday seasons.
    • Reliability: Data loss is unacceptable; durability is required.
    • Consistency: Strong consistency for booking writes, eventual consistency for read‑only search.

If the interviewer wants a narrower focus (e.g., only the booking flow), adapt the scope accordingly.

2. Identify Core Entities and Relationships

A clear data model helps you reason about APIs and storage.

EntityKey AttributesRelationships
Hotelhotel_id, name, address, ratingHas many RoomTypes
RoomTypetype_id, hotel_id, description, capacity, base_priceBelongs to Hotel, has many RoomInventory
RoomInventoryinventory_id, type_id, date, available_countTracks availability per RoomType per day
Bookingbooking_id, user_id, hotel_id, check_in, check_out, status, total_priceReferences RoomType (one or many)
Paymentpayment_id, booking_id, amount, status, provider_txn_idOne‑to‑one with Booking

The RoomInventory table is the heart of concurrency control – each day‑room combination has a count that you decrement atomically when a reservation is confirmed.

3. Sketch the High‑Level Architecture

+-------------------+        +-------------------+        +-------------------+
|   Client (Web/   |  API   |   API Gateway     |  RPC   |   Booking Service |
|   Mobile)        | <----> | (Auth, Rate‑lim) | <----> | (Business Logic) |
+-------------------+        +-------------------+        +-------------------+
                                 |                               |
                                 |                               |
                                 v                               v
                        +-------------------+        +-------------------+
                        |   Search Service |        |   Payment Service |
                        +-------------------+        +-------------------+
                                 |                               |
                                 v                               v
                        +-------------------+        +-------------------+
                        |   Cache (Redis)  |        |   DB (Postgres)   |
                        +-------------------+        +-------------------+
  • API Gateway handles authentication, rate‑limiting, and routing.
  • Booking Service owns the write path: create, update, cancel bookings, and updates inventory.
  • Search Service reads from a denormalized view (e.g., ElasticSearch) for fast filtering.
  • Cache stores hot availability data to reduce DB load.
  • Primary DB holds authoritative data; you may use a relational store for transactional consistency.

4. Design the Core API

Keep the API surface small but expressive.

GET /hotels?city=Paris&check_in=2024-12-20&check_out=2024-12-25&guests=2
POST /bookings
PUT /bookings/{id}
DELETE /bookings/{id}
GET /bookings/{id}

Create Booking request (simplified)

{
  "user_id": "u123",
  "hotel_id": "h456",
  "rooms": [{"type_id": "t1", "quantity": 2}],
  "check_in": "2024-12-20",
  "check_out": "2024-12-25",
  "payment_token": "tok_abc"
}

The service validates:

  1. Dates are valid and in the future.
  2. Requested quantity does not exceed RoomInventory.available_count for each day.
  3. Payment succeeds (or is authorized).
  4. If all checks pass, it writes a Booking row and atomically decrements inventory.

5. Deep Dive: Concurrency & Double‑Booking Prevention

5.1 Pessimistic Locking (Row‑level)

  • Begin a transaction.
  • SELECT ... FOR UPDATE on the relevant RoomInventory rows.
  • Verify counts, then UPDATE the counts.
  • Commit.

Pros: Simple, strong consistency. Cons: Locks can become a bottleneck under high traffic; long‑running transactions increase latency.

5.2 Optimistic Concurrency with Version Numbers

  • Store a version column on RoomInventory.
  • Read the current version and count.
  • Attempt an UPDATE with WHERE version = old_version.
  • If 0 rows affected, retry.

Pros: Higher throughput, less lock contention. Cons: Requires retry logic; worst‑case latency can spike during heavy contention.

5.3 Distributed Locks (e.g., Redis RedLock)

  • Acquire a lock per hotel_id:date key.
  • Perform the same check‑then‑update logic.
  • Release lock.

Useful when you have multiple service instances and want to avoid DB‑level row locks. However, you must handle lock failures and ensure the lock service itself is highly available.

6. Scaling the Search Path

Search is read‑heavy and latency‑sensitive.

  • Denormalize: Create a daily availability document per hotel that aggregates inventory counts. Update it via change‑data‑capture (CDC) from the primary DB.
  • Index: Store the documents in a search engine (e.g., OpenSearch) to support filters on price, amenities, and rating.
  • Cache: Frequently queried date ranges can be cached in Redis for sub‑millisecond responses.

Trade‑off: The denormalized view introduces eventual consistency; a user might see a room as available for a few seconds after it has been booked. In most consumer scenarios, this window is acceptable, but you can mitigate it by short TTLs or by checking inventory again at checkout.

7. Typical Follow‑Up Questions

QuestionWhat the interviewer probes
"How would you handle cancellations?"Discuss idempotent cancellation, inventory roll‑back, and refund flow.
"What if a hotel wants to offer dynamic pricing?"Explain moving price calculation to a pricing service, feeding it into the search index.
"How would you migrate from a monolith to microservices?"Talk about extracting the booking domain, using a shared event bus, and incremental rollout.
"What if we need to support group bookings across multiple hotels?"Suggest a higher‑level aggregation service that composes availability from several hotels and handles atomicity via a saga pattern.
"How do you ensure data durability across regions?"Mention multi‑region replication for the primary DB and read‑only replicas for disaster recovery.

Be ready to discuss pros/cons and how you would measure the impact of each decision.

8. Trade‑offs Summary

AspectOption AOption B
Consistency for writesPessimistic locking (strong)Optimistic versioning (higher throughput)
Search latencyDirect DB queries (simpler)Denormalized search index (faster)
Operational complexitySingle monolithMicroservices with event streaming
CostLower infra footprintMore services, higher ops cost

Choose the combination that matches the product’s SLA and the team’s maturity.

How to practice this

  1. Mock the interview – Use a timer and walk through the steps aloud. Record yourself and replay to catch filler words.
  2. Build a mini prototype – Implement a simple API with an in‑memory store, then add a Redis lock layer to feel the concurrency challenges.
  3. Use Call Assistant to rehearse your answer, letting it capture your resume points and keep the conversation on track while you focus on the design details.

FAQ

  • What are the most common pitfalls when designing a reservation system? Over‑engineering the search layer before solidifying the booking flow, and ignoring race conditions that lead to double‑booking.
  • Do I need a separate pricing service? Not for a basic MVP; you can embed price in the room type. Introduce a dedicated service when you need dynamic pricing, promotions, or third‑party rate feeds.
  • How much data does the inventory table hold? It grows with the number of hotels, room types, and days you keep inventory for. A typical approach is to keep a rolling window of a year, which is manageable for relational stores.
  • Is eventual consistency acceptable for availability? In most consumer‑facing apps, a few‑second window is fine, especially if you re‑validate at checkout. If the business demands zero‑stale reads, you must keep the write path strongly consistent and accept higher latency.

Frequently asked questions

What are the most common pitfalls when designing a reservation system?

Over‑engineering the search layer before solidifying the booking flow, and ignoring race conditions that lead to double‑booking.

Do I need a separate pricing service?

Not for a basic MVP; you can embed price in the room type. Introduce a dedicated service when you need dynamic pricing, promotions, or third‑party rate feeds.

How much data does the inventory table hold?

It grows with the number of hotels, room types, and days you keep inventory for. Keeping a rolling window of about a year is typical and fits comfortably in a relational store.

Is eventual consistency acceptable for availability?

In most consumer‑facing apps, a few‑second window is fine, especially if you re‑validate at checkout. If the business demands zero‑stale reads, you must keep the write path strongly consistent and accept higher latency.

#system design#hotel reservation#architecture#concurrency#scalability#a hotel reservation system