Design a Hotel Booking System
Build a system that handles millions of hotel listings, searches, and bookings while preventing double-bookings.
Related Concepts: Transactions, Caching, Database Scaling
Step 1: Requirements and Scope
Functional Requirements
| Requirement | Description |
|---|---|
| Search | Find available hotels by location, dates, guests |
| View listing | See hotel details, photos, reviews, pricing |
| Book | Reserve a room for specific dates |
| Manage booking | View, modify, or cancel reservations |
| Inventory management | Hotels update availability and pricing |
Non-Functional Requirements
| Requirement | Target | Rationale |
|---|---|---|
| Search latency | < 200ms | User experience |
| Booking consistency | No double-booking | Legal and trust requirements |
| Availability | 99.99% | Revenue-critical |
| Scale | 5M+ hotels, 1M+ bookings/day | Global travel market |
Scale Estimation
- Hotels: 5 million properties
- Rooms: 50 million rooms total (10 per hotel average)
- Daily searches: 500 million
- Daily bookings: 1 million
- Peak booking rate: 50 bookings/second
Step 2: High-Level Architecture
Step 3: Data Model
Core Entities
Availability Tracking
Option 1: Date Range Approach Store availability as date ranges with start and end dates per room. For example, room-1 is available from January 1 to January 15, then again from January 20 to February 1. This approach leads to complex queries and is difficult to update when bookings fragment the ranges.
Option 2: Daily Inventory (Recommended) Store one row per room per date, tracking total inventory and booked count. For example, room-1 on January 1 has 10 total rooms with 3 booked (7 available), on January 2 has 10 total with 5 booked, and on January 3 has 10 total with 10 booked (sold out). This approach uses simple queries and easy updates.
Pre-generate 365 days of availability per room. 50M rooms x 365 days = 18B rows. Partition by date.
Step 4: Search
Search Flow
Search Index Design
The Elasticsearch index for hotels contains fields for fast filtering:
- hotel_id: Unique identifier
- name: Hotel display name (e.g., "Grand Hotel NYC")
- location: Latitude and longitude for geo queries
- city: City name for location filtering
- star_rating: Numeric rating (1-5)
- amenities: Array of features like wifi, pool, gym
- price_range: Minimum and maximum nightly rates
- review_score: Average guest rating
- room_types: Array of room options with type and maximum guests per room
Availability in Search
| Approach | Mechanism | Advantages | Disadvantages |
|---|---|---|---|
| Post-filter | Search ES, then check DB for availability | Simple search | Slow, may return no results |
| Denormalize | Store availability in ES | Fast search | Sync complexity, staleness |
Recommendation: Hybrid approach. Store "likely available" flag in ES (updated hourly). Post-filter top candidates against real-time inventory. Cache availability aggressively.
Step 5: Booking Flow
The Double-Booking Problem
Two users try to book the last room simultaneously. Without proper handling, both succeed.
Solution: Pessimistic Locking
Lock the inventory rows before checking:
SELECT ... FOR UPDATE locks the rows. Second transaction waits. Once first commits, second sees updated state.
Optimistic Locking Alternative
For lower contention scenarios, use version-based optimistic locking. The update increments both the booked count and version number, but only succeeds if the current version matches the expected value and inventory is available. If another transaction modified the row (changing the version), the update affects zero rows, signaling the need to retry or fail gracefully.
| Approach | Use Case | Trade-off |
|---|---|---|
| Pessimistic | High contention (popular rooms) | Blocking waits |
| Optimistic | Low contention (most rooms) | Retry overhead |
Recommendation: Optimistic by default, pessimistic for hot inventory (last few rooms).
Booking Transaction
Hold inventory until payment confirms, but not indefinitely. Set a timeout (10-15 minutes).
Step 6: Inventory Management
Real-Time Updates
Update flow:
- Hotel system sends update
- Queue ensures durability
- Processor updates database
- Cache invalidated
- Search index updated async
Overbooking Handling
Hotels intentionally overbook, expecting cancellations. The system supports this by tracking both actual inventory (10 rooms) and an oversell limit (11 bookings allowed). Available rooms are calculated as oversell limit minus booked count. When booked count reaches 11 (even though only 10 rooms exist), availability shows zero. Hotels configure the oversell policy per room type based on historical cancellation rates.
Step 7: Pricing
Dynamic Pricing
Prices change based on:
- Day of week (weekends cost more)
- Season (holidays cost more)
- Demand (surge pricing)
- Lead time (last minute discounts)
Price Storage
Store base prices and modifiers separately. For example, a room with a base price of $100 per night might have modifiers for day of week (Saturdays at 1.2x multiplier), seasonality (December 20 through January 5 at 1.3x multiplier), and occupancy-based pricing (when above 80% occupancy at 1.15x multiplier).
The final price is calculated at query time by applying all applicable modifiers to the base price. This approach allows flexible pricing rules without storing every date and scenario permutation.
Step 8: Cancellation and Modifications
Cancellation Flow
Always release inventory back to available pool on cancellation.
Modification Handling
Modifications are effectively cancel + rebook:
- Lock new dates
- Verify availability
- Calculate price difference
- Process payment adjustment
- Release old dates
- Confirm new dates
All in one transaction to prevent inconsistencies.
Production Examples
| System | Notable Design Choice |
|---|---|
| Booking.com | Aggressive caching, extensive A/B testing |
| Airbnb | Request-to-book for some listings, instant for others |
| Expedia | Aggregates multiple sources, complex pricing |
| HRS | Corporate booking focus, negotiated rates |
Summary: Key Design Decisions
| Decision | Options | Recommendation |
|---|---|---|
| Availability model | Date ranges, daily rows | Daily rows (simpler queries) |
| Search | Single source, federated | ES + post-filter availability |
| Locking | Pessimistic, optimistic | Optimistic default, pessimistic for hot |
| Payment timing | Pre-auth, full charge | Pre-auth with timeout |
| Price storage | Pre-computed, calculated | Base + modifiers (flexible) |