Hotel Reservation System Design Template
Hotel reservation system: room-type-per-night inventory, atomic multi-night holds, rate plans, LOS/CTA/CTD restrictions, overbooking model, channels, PMS
How to use: Use it when designing a hotel or lodging reservation system rather than seat-based ticketing. Fix the inventory unit (room type per property per night) and the portfolio numbers in section 3 first, then agree overbooking limits with revenue management. The main pitfall is booking nights one by one, which leaves stays with some nights reserved and others lost.
Preview
System Design: [Hotel reservation system name]
Reference design for a hotel group's central reservation system: inventory counted per room type and per night, multi-night stays booked atomically, rate plans with derived prices and stay restrictions (minimum length of stay, closed to arrival, closed to departure), an overbooking policy computed from show-up rates and walk costs, group blocks, and distribution of availability and rates to channels. It differs from seat booking in that nothing is sold by identity: a stay consumes one unit of a room type on each night, and the room number is assigned later. It follows the structure of the System Design Document template, adds a 45-minute interview path, access-pattern and failure-mode tables and a cited "How real systems do it" box, and is pre-filled with a worked design to adapt. Replace every
[bracketed]value and every row marked Example; values marked Example are assumptions, not benchmarks.
Document control
| Field | Value |
|---|---|
| Document ID | [SDD-HR-001] |
| System / feature | [Central reservation system] |
| Version | [0.1] |
| Status | [Draft / In review / Approved / Implemented / Superseded] |
| Owner | [@owner] |
| Author(s) | [@author] |
| Reviewers | [@reviewer-1], [@reviewer-2] |
| Last updated | YYYY-MM-DD |
| Classification | [Internal] |
| Difficulty | Intermediate |
| Revenue management owner | [@name] |
| Related | [Payment ledger design], [Channel integration specs], [ADR: overbooking limits] |
Approvals
| Role | Name | Decision | Date |
|---|---|---|---|
| Tech lead | [@name] | [Approved / Changes requested] | YYYY-MM-DD |
| Staff / principal engineer | [@name] | [Decision] | YYYY-MM-DD |
| Revenue management | [@name] | [Decision] | YYYY-MM-DD |
| Hotel operations | [@name] | [Decision] | YYYY-MM-DD |
| Security and payments | [@name] | [Decision] | YYYY-MM-DD |
Revision history
| Version | Date | Author | Change summary |
|---|---|---|---|
| 0.1 | YYYY-MM-DD | [@name] | First draft for design review |
| 0.2 | YYYY-MM-DD | [@name] | Example: Overbooking limit capped by walk probability, not only expected profit |
| 0.3 | YYYY-MM-DD | [@name] | Example: Group blocks release to general inventory at cutoff |
| 1.0 | YYYY-MM-DD | [@name] | Example: Approved |
Contents
1. Overview
1.1 Problem statement
[Example: Our 1 200 hotels each run their own property management system, and the website, the call centre and two travel agencies each keep a copy of availability that is refreshed every 15 minutes. On busy weekends we sell rooms that no longer exist, front desks walk guests without a policy, and a three-night booking sometimes holds Friday and Saturday but loses Sunday. Rate changes take hours to reach all channels, so the same room shows three prices.]
1.2 Proposed solution
[Example: A central reservation system owns future inventory as counters per property, room type and night. A booking reserves one unit on each night of the stay in a single transaction on the property's shard, or fails as a whole. Rate plans are defined once (a base rate plus derived plans and restrictions) and priced per night. Availability and prices for search come from a precomputed calendar that is updated by events within seconds; the booking path always re-checks the counters. Overbooking limits per room type and night come from revenue management and are enforced as part of the counter check. Channels receive availability, rates and restrictions by push on every change, and reservations from channels enter through the same booking path. The PMS receives reservations and sends back in-house changes and out-of-order rooms.]
1.3 Decisions requested from reviewers
| ID | Question | Recommended option | Section |
|---|---|---|---|
| Q-01 | Example: Unit of inventory? | Counter per property, room type and night; no room numbers before check-in | 5, 7.1 |
| Q-02 | Example: How to book several nights safely? | One transaction with conditional decrements on all nights, rows locked in date order | 7.2 |
| Q-03 | Example: Overbooking limit? | Expected-profit optimum, capped so the chance of walking any guest stays at or below 10% | 7.5 |
| Q-04 | Example: Channel inventory? | One shared pool with last-room availability, not fixed allotments per channel | 7.6 |
| Q-05 | Example: Search freshness? | Precomputed calendar, lag p99 at or below 5 s; booking re-checks | 7.4 |
| [Q-nn] | [Question] | [Option] | [Section] |
1.4 45-minute interview path
Use this order when the document serves an interview or a short design review. The written sections below are the long form of each step.
| Minutes | Cover | Output on the whiteboard |
|---|---|---|
| 0 to 5 | Scope: search, book, modify, cancel; one hotel group; room types, not rooms; below-the-line items (2.3) | The inventory unit "room type x night" |
| 5 to 10 | Estimates: rooms, bookings per day, searches per second, inventory rows (section 3) | Four numbers, "reads dominate by 500 to 1, writes are tiny" consequence |
| 10 to 15 | API: availability search, hold, confirm, modify, cancel with idempotency (section 4) | Five calls |
| 15 to 25 | High-level design: booking service on inventory store, calendar cache for search, channel and PMS sync (section 6) | One diagram with a read path and a write path |
| 25 to 40 | One deep dive: multi-night atomic booking (default, 7.2), or rate plans and restrictions (7.3), or overbooking (7.5) | Options, choice, reason |
| 40 to 45 | Failures (channel sync lag, PMS offline), the 10x row in section 10 | Two failure rows |
Start the default deep dive with the naive answer: "for each night, read availability, then insert the booking". Show two guests booking the last Saturday room, one for Friday and Saturday and one for Saturday and Sunday, both reading 1 available. Then compare a lock per night, a conditional update per night in one transaction, and a single property lock, and explain why rows are locked in date order.
2. Requirements
2.1 Core functional requirements
| ID | Requirement | Priority |
|---|---|---|
| FR-01 | Example: Search availability and prices by property or destination, dates, guests | Must |
| FR-02 | Example: Book a stay of 1 to 30 nights in one room type and rate plan, all nights or none | Must |
| FR-03 | Example: Rate plans with derived prices, inclusions, cancellation and deposit policies | Must |
| FR-04 | Example: Restrictions per night: minimum and maximum length of stay, closed to arrival, closed to departure, stop-sell | Must |
| FR-05 | Example: Modify dates or room type; cancel within policy | Must |
| FR-06 | Example: Overbooking within limits set per room type and night | Must |
| FR-07 | Example: Group blocks with a cutoff date after which unused rooms return to general inventory | Should |
| FR-08 | Example: Push availability, rates and restrictions to channels; accept their reservations | Must |
| FR-09 | Example: Send reservations to the PMS; receive out-of-order rooms and in-house changes | Must |
| [FR-nn] | [Requirement] | [Must / Should / Could] |
2.2 Non-functional requirements and invariants
| ID | Quality | Target | Measured as |
|---|---|---|---|
| NFR-01 | No oversell beyond policy | Example: For every property, room type and night: sold plus held never exceeds rooms minus out of order plus the overbooking limit | Nightly audit |
| NFR-02 | Atomic stays | Example: No booking with some nights reserved and others not | Audit of booking nights against counters |
| NFR-03 | Search latency | Example: p95 at or below 300 ms for a property calendar | Synthetic and real-user monitoring |
| NFR-04 | Booking latency | Example: p99 at or below 500 ms for hold and confirm, excluding payment | Service timing |
| NFR-05 | Freshness | Example: Calendar and channels updated within 5 s p99 of a change | Event age |
| NFR-06 | Availability (booking path) | Example: 99.95% monthly | SLO |
| [NFR-nn] | [Quality] | [Target] | [Measurement] |
2.3 Below the line (out of scope)
Name these explicitly so reviewers and interviewers do not pull the design toward them.
- [Example: revenue management forecasting and price optimisation (this system consumes their output)]
- [Example: room assignment, housekeeping and check-in (PMS)]
- [Example: loyalty points accrual (separate service fed by stay events)]
- [Example: seat-based events and onsales (see Ticket and Hotel Booking System Design (
system-design/design-ticket-booking))] - [Item deferred, with the release that will take it]
3. Capacity estimation
3.1 Assumptions
| Input | Symbol | Value | Source |
|---|---|---|---|
| Properties | Example: 1 200 | Portfolio | |
| Average rooms per property | Example: 150 | Portfolio | |
| Room types per property | Example: 8 | Product catalogue | |
| Rate plans per room type | Example: 12 | Revenue management | |
| Booking horizon | Example: 730 nights | Product rule | |
| Average occupancy | Example: 70% | Last 12 months | |
| Average length of stay | Example: 2.2 nights | Last 12 months | |
| Share of bookings later cancelled | Example: 25% | Last 12 months | |
| Availability queries per gross booking (look-to-book) | Example: 500 | Channel and web logs |
3.2 Bookings and searches
Rooms in the portfolio: . Occupied room nights per day divided by length of stay gives arrivals, which equal net bookings in a steady state:
Booking writes are about per second on average and about 9 per second during a promotion at 10 times the average. Each booking touches night rows, about 168 000 night-row updates a day.
3.3 Inventory and rate data
| Data | Calculation | Result |
|---|---|---|
| Inventory rows | 7 008 000 rows; at 200 bytes about 1.4 GB | |
| Rate rows (price and restrictions per plan and night) | 84 096 000 rows; at 100 bytes about 8.4 GB | |
| Search calendar | One entry per rate row, already priced | about 8.4 GB, fits in a replicated in-memory cache |
3.4 Summary
| Quantity | Estimate | Design consequence |
|---|---|---|
| Searches | about 1 300 queries/s at peak | Separate read path from a precomputed calendar |
| Bookings | under 10/s even in a promotion | One relational cluster sharded by property; contention, not volume, is the issue |
| Inventory | 7 million rows, 1.4 GB | Small; partition by property for single-shard stays |
| Rates | 84 million rows, 8.4 GB | Store base and derivation rules; materialise for search only |
4. API design
4.1 Endpoints
| Method and path | Purpose | Notes |
|---|---|---|
GET /v1/properties/{id}/availability?checkin=&checkout=&adults= | Room types and rate plans with total price for the stay | From the calendar cache; may be seconds stale |
POST /v1/holds | Reserve inventory for a stay for 15 minutes | Requires Idempotency-Key; re-checks counters |
POST /v1/reservations | Confirm a hold with guest and guarantee details | Payment by token only |
PATCH /v1/reservations/{id} | Change dates, room type or guests | New nights held before old nights released, in one transaction |
POST /v1/reservations/{id}/cancel | Cancel; returns the penalty under the rate plan policy | Idempotent |
PUT /v1/inventory/{property}/{room_type}/{date} | Revenue management and operations: overbooking limit, stop-sell, out-of-order count | Audited |
The Idempotency-Key header follows the pattern of
draft-ietf-httpapi-idempotency-key-header-08
(22 October 2026, an Internet-Draft, not an RFC). Errors use problem details
(RFC 9457).
4.2 Request and response examples
Hold request:
{"property_id": "PRP-0417", "room_type": "KING-DLX", "rate_plan": "BAR", "checkin": "2027-03-11", "checkout": "2027-03-14", "adults": 2}
Hold response with the per-night breakdown:
{"hold_id": "HLD-88213", "expires_at": "2027-02-02T10:15:00Z", "currency": "USD", "nights": [{"date": "2027-03-11", "price": 180}, {"date": "2027-03-12", "price": 220}, {"date": "2027-03-13", "price": 240}], "total": 640, "cancellation_policy": "Free until 48 hours before arrival, property local time"}
A hold that fails on any night returns 409 with the failing night and the
reason (sold_out, closed_to_arrival, min_los_not_met), so the client
can suggest other dates.
5. Data model
5.1 Entities
Inventory and rates are keyed by property-local stay dates (a night is a date, not a timestamp). A reservation has one row per night it consumes.
5.2 Storage choices
| Data | Store | Key | Why |
|---|---|---|---|
| Inventory nights, reservations, holds | Example: Relational database sharded by property_id | (property_id, room_type, stay_date) | Multi-row transactions on one shard |
| Rate plans and rate nights | Same shard as the property | (property_id, room_type, rate_plan, stay_date) | Pricing in the booking transaction reads local rows |
| Search calendar | Example: Replicated in-memory cache | (property_id, room_type, rate_plan) with a 730-night array | One key read per room type and plan |
| Change events | Partitioned log | property_id | Ordered per property for calendar, channels and PMS |
| Payment tokens | Payment provider vault | Token | Card data never stored in this system |
5.3 Access patterns
| Access pattern | Frequency | Key or index that serves it | Partition key rationale |
|---|---|---|---|
| Show prices for a stay at one property | about 1 300/s at peak | Calendar cache by (property_id, room_type, rate_plan) | Search never touches the booking database |
| Hold a multi-night stay | under 10/s | Inventory rows (property_id, room_type, stay_date range) | All nights of a stay are on the property's shard, so one local transaction |
| Price the stay inside the hold | Every hold | Rate night rows on the same shard | No cross-shard read in the transaction |
| Modify dates | Rare | Old and new night rows of the same property | Still one shard |
| Find a guest's reservations | Support, guest app | Secondary index on guest ID, or a separate lookup table | Cross-property queries go to a read replica |
| Expire holds | Every minute | Index on hold.expires_at per shard | Sweeper per shard |
| Nightly audit | Daily | Full scan per shard of tomorrow's nights | Bounded: 8 room types x 1 night per property |
6. High-level design
| Component | Responsibility | Scales by |
|---|---|---|
| Reservation API | Authentication, routing of search to cache and writes to booking | Stateless instances |
| Booking service | Holds, confirms, modifications, cancellations; enforces counters and restrictions | Stateless, routes by property shard |
| Inventory and reservation DB | Source of truth for future inventory and reservations | Shards by property |
| Calendar builder | Applies change events to the search calendar | Partitions of the change log |
| Channel push | Sends availability, rate and restriction changes per channel format | Per channel worker pools |
| PMS connector | Two-way sync with each hotel's PMS | Per property queues |
7. Detailed design
The deep dives in this section, each with options, choice and reason:
| Deep dive | Options considered | Choice | Reason | Section |
|---|---|---|---|---|
| Multi-night booking | Read then write per night; lock per night; conditional update per night in one transaction | Conditional updates in one transaction, rows in date order | All or nothing, no deadlock, no long locks | 7.2 |
| Pricing | Store every price; derive at read time; derive at write time and materialise | Base rates stored, derived plans materialised into the calendar | One source to change, fast search | 7.3 |
| Overbooking | None; fixed percentage; computed per room type and night | Computed limit, capped by walk risk | Revenue without uncontrolled walks | 7.5 |
| Channel inventory | Fixed allotments; shared pool | Shared pool with push on change | No stranded rooms; one counter | 7.6 |
7.1 Inventory model
Sell room types, not rooms. A room number is chosen at or near check-in by the PMS, which lets the hotel absorb early departures, late check-outs and maintenance without touching reservations. For each property, room type and night:
A booking is allowed on a night only if available is at least 1 and the
night is not stop-sold. NFR-01 is this inequality checked for every row.
7.2 Multi-night atomic hold
A stay from 11 to 14 March consumes the nights of 11, 12 and 13 March. The hold runs one transaction on the property's shard:
BEGIN;
-- lock the nights in date order first, so overlapping stays queue in the same order
SELECT stay_date FROM inventory_night
WHERE property_id = 'PRP-0417' AND room_type = 'KING-DLX'
AND stay_date >= DATE '2027-03-11' AND stay_date < DATE '2027-03-14'
ORDER BY stay_date
FOR UPDATE;
UPDATE inventory_night
SET held = held + 1, version = version + 1
WHERE property_id = 'PRP-0417' AND room_type = 'KING-DLX'
AND stay_date >= DATE '2027-03-11' AND stay_date < DATE '2027-03-14'
AND stop_sell = FALSE
AND physical - out_of_order + overbook_limit - sold - held >= 1;
-- the service checks that the row count equals the number of nights (3);
-- if not, ROLLBACK and report the first failing night
INSERT INTO hold (hold_id, property_id, room_type, checkin, checkout, expires_at)
VALUES ('HLD-88213', 'PRP-0417', 'KING-DLX', DATE '2027-03-11', DATE '2027-03-14', now() + INTERVAL '15 minutes');
COMMIT;
The explicit SELECT with ORDER BY stay_date FOR UPDATE locks the nights in
date order (an UPDATE over a range does not promise any lock order), so two
overlapping stays always lock shared nights in the same order and cannot
deadlock; the locks are held only for the few milliseconds of the
transaction, never across payment. If the updated row count is less than the number of nights, at
least one night is full or closed, and the transaction rolls back.
Confirming a hold moves the count from held to sold on each night and
writes reservation nights with their prices. A sweeper releases expired holds
by the reverse update. A modification holds the new nights and releases the
old nights in the same transaction, so a guest who moves a stay never ends up
with neither booking.
7.3 Rate plans and restrictions
Revenue management sets a base rate (best available rate, BAR) per room type and night. Other plans derive from it:
| Plan | Derivation | Policy | Example for Thu 180, Fri 220, Sat 240 |
|---|---|---|---|
BAR | Base | Free cancellation until 48 h before arrival | 640 for 3 nights |
NRF | BAR minus 15% | Non-refundable, charged at booking | 544 |
BB2 | BAR plus breakfast 18 per guest per night | As BAR | 640 + 3 x 2 x 18 = 748 |
MEMBER | BAR minus 10%, members only | As BAR | 576 |
The stay price is the sum of the nightly prices, each from that night's rate row, so a stay over a price change mixes prices correctly. Restrictions are evaluated against the arrival and departure dates:
| Restriction | Checked on | Rule |
|---|---|---|
| Minimum length of stay (MinLOS) | Arrival night | Stay length at least the value set on the arrival night |
| Maximum length of stay (MaxLOS) | Arrival night | Stay length at most the value |
| Closed to arrival (CTA) | Arrival night | No stays may start on this night |
| Closed to departure (CTD) | Departure date | No stays may end on this date |
| Stop-sell | Every night | No new holds for the plan or room type |
Restrictions make availability depend on the stay, not only on the nights: a Saturday with MinLOS 2 is available for Friday to Sunday but not for Saturday alone. The calendar therefore stores restrictions next to prices, and the search evaluates them per requested stay.
7.4 Search calendar
The calendar builder consumes change events per property and keeps, for each (property, room type, rate plan), an array of 730 entries: available count, price, MinLOS, MaxLOS, CTA, CTD. A search for one property reads at most 8 x 12 = 96 keys and evaluates the stay in memory. Lag is the event pipeline lag (NFR-05). The cache can be wrong for a few seconds; the hold in 7.2 is always the final check, and a failed hold returns the failing night so the page can refresh.
7.5 Overbooking policy
Some guests do not arrive and some cancel too late to resell. Selling a few rooms above capacity raises expected revenue but risks walking a guest (relocating them to another hotel at the group's cost). With capacity , booking limit and independent show probability , arrivals follow . With the margin of a room night lost when a room stays empty and the cost of walking one guest, the expected contribution is:
Example: , , = 150 (room rate 180 minus 30 variable cost), = 450 (relocation, transport, compensation and goodwill), all in USD:
| Booking limit | Expected contribution | Expected walks | Probability of at least one walk | Expected empty rooms |
|---|---|---|---|---|
| 100 | 13 800.0 | 0.000 | 0.000 | 8.00 |
| 104 | 14 328.2 | 0.040 | 0.029 | 4.36 |
| 105 | 14 425.2 | 0.108 | 0.071 | 3.51 |
| 106 | 14 482.4 | 0.243 | 0.140 | 2.72 |
| 107 | 14 483.9 | 0.470 | 0.239 | 2.03 |
| 108 | 14 418.0 | 0.810 | 0.359 | 1.45 |
The expected-contribution optimum is (an overbooking limit of 7),
but it walks at least one guest on about 24% of such nights. With a policy cap
of 10% on the probability of any walk, the limit is (limit 5), which
gives up 58.7 of expected contribution per night (14 483.9 minus 14 425.2) to
cut the walk probability from 0.239 to 0.071. Revenue management approves the
cap; the system stores overbook_limit per room type and night and enforces
it in 7.1. The binomial model assumes guests decide independently; groups
and event nights break that assumption, so set lower limits there.
When arrivals exceed rooms, the walk workflow chooses which reservation to relocate (never loyalty top tiers or guests already walked on this trip, as Example rules), books a comparable nearby hotel, pays transport and compensation, and records the walk for the next forecast. Consumer rules on relocation and disclosure differ by country; check the local rules and a qualified professional. This is not legal advice.
7.6 Channels, group blocks and the PMS
- Shared pool: channels sell from the same counters (last-room availability), so a room is never stranded in one channel's allotment. The channel push sends availability, rates and restrictions on every change, batched per property for 1 s. A channel that sells on a stale count sends a reservation that fails the hold; the gateway answers with a rejection the channel understands, and the lag is tracked per channel (9.3).
- Group blocks: a block reserves rooms for a group as its own counter per night, taken out of general inventory. Guests book from the block until the cutoff date; at cutoff a job returns unused block rooms to general inventory in one transaction per property.
- PMS: the central system owns future reservations; the PMS owns in-house
stays and room assignment. Out-of-order rooms from the PMS raise
out_of_ordercounts and can create a negative availability that triggers the walk workflow before arrival day rather than at the desk.
7.7 How real systems do it
How real systems do it (paraphrased, not copied). Rothstein's paper "Hotel overbooking as a Markovian sequential decision process" (Decision Sciences, 1974) describes a general hotel reservation system and models booking acceptance as a sequence of decisions; applied to sample data from a real hotel, cancellations were frequent enough that the model prescribed a substantial amount of overbooking. Talluri and van Ryzin's book "The Theory and Practice of Revenue Management" (Springer, 2004) treats overbooking and network revenue management, where a product such as a multi-night stay uses capacity on several resources at once, which is why 7.2 books every night of a stay together. Cross, Higbie and Cross, "Revenue Management's Renaissance" (Cornell Hospitality Quarterly, 2009), argue that hotel revenue management should move beyond room inventory control to total revenue, price elasticity and measures that compare performance with competitors. Read the originals in 12.3; this box keeps only the general ideas.
8. Scalability and performance
| Concern | Approach | Limit before redesign |
|---|---|---|
| Search | Calendar cache, replicated per region | Example: 20 000 queries/s with current nodes |
| Holds | Sharded by property, short transactions | Contention on one property's popular nights during a promotion |
| Channel push | Batched per property, per-channel workers | Channel rate limits |
| Rate updates | Derived plans recomputed per changed base night only | Bulk yearly loads run off-peak |
| Audit | Per shard, tomorrow's nights only, plus weekly full horizon | [Limit] |
Latency budget for a hold (target p99 500 ms):
| Step | Budget |
|---|---|
| API, authentication, idempotency lookup | 40 ms |
| Restriction check on rate nights | 30 ms |
| Inventory transaction (up to 30 rows) | 80 ms |
| Event publish | 20 ms |
| Headroom | 330 ms |
9. Reliability, security and operations
9.1 Failure modes
| Component | Failure | Detection | Blast radius | Mitigation | RTO |
|---|---|---|---|---|---|
| Property shard primary | Failover | Error rate per shard | Properties on that shard cannot book for seconds | Synchronous replica; idempotent retries by clients | Example: 30 s |
| Calendar builder | Falls behind | Event age above 5 s | Search shows stale availability; holds still correct | Scale consumers; banner on search if lag above 60 s | Example: 5 min |
| Channel push | Channel endpoint down | Push error rate per channel | Channel sells on stale data | Retry with backoff; stop-sell that channel if lag passes 15 min | Example: 15 min |
| PMS connector | Hotel PMS offline | Heartbeat per property | One hotel's out-of-order and in-house updates | Queue changes; front desk works from PMS; reconcile on reconnect | Example: hours, local |
| Hold sweeper | Stops | Count of expired holds not released | Inventory locked by abandoned holds | Alert; holds also checked for expiry at read time | Example: 5 min |
| Rate load | Wrong prices loaded | Price change monitor (large jumps) | Plans derived from the wrong base | Versioned rate loads; rollback to previous version | Example: 10 min |
| [Component] | [Failure] | [Signal] | [Scope] | [Mitigation] | [Time] |
9.2 Security and abuse
| Threat | Control |
|---|---|
| Card data exposure | Tokenisation at the payment provider; this system stores tokens only, keeping it out of cardholder data scope where possible |
| Inventory hoarding by bots (holds never confirmed) | Holds per session and per IP limited; short TTL; holds by channels count against their quota |
| Rate scraping | Rate limits per client; signed partner API keys |
| Guest data access | Role-based access; reservation lookup needs reservation ID plus surname or authenticated account |
| Tampering with overbooking limits | Changes audited and limited to revenue management roles |
9.3 Observability
| Signal | Metric or log | Alert threshold |
|---|---|---|
| Oversell beyond policy | Audit violations (NFR-01) | Any |
| Hold failure reasons | Sold out, restriction, error, by property | Error share above 1% |
| Calendar lag | Event age at cache write | p99 above 5 s |
| Channel lag and rejections | Per channel | Lag above 60 s or rejection rate doubling |
| Walks | Walked guests per property and night | Above forecast from 7.5 |
9.4 Cost estimate
| Item | Unit cost | Quantity | Monthly cost |
|---|---|---|---|
| Database shards with replicas | [$/node-month] | Example: 8 shards x 3 | [$] |
| Calendar cache nodes | [$/node-month] | Example: 6 | [$] |
| Event log and consumers | [$/month] | [Size] | [$] |
| Channel connectivity fees | [/month] | [Volume] | [$] |
| Total | [$] |
10. Trade-offs and alternatives
| Decision | Options | Chosen | Why | What we give up |
|---|---|---|---|---|
| Unit of sale | Specific room; room type per night | Room type per night | Flexibility for operations, simple counters | Guests cannot pick a room number at booking |
| Concurrency | Pessimistic locks across payment; conditional updates plus hold TTL | Conditional updates and holds | Short transactions, no lock across network calls | Abandoned holds block inventory for 15 min |
| Partitioning | By date; by property | By property | Stays and modifications stay on one shard | Cross-property guest queries need an index or replica |
| Channel inventory | Allotments; shared pool | Shared pool | No stranded rooms | Stale channels may sell into a full night and get rejected |
| Prices | All materialised; all derived at read; hybrid | Base stored, derived materialised for search | One place to change rates, fast reads | Calendar rebuild after base changes |
| What changes at 10x / 0.1x | 10x (12 000 properties, about 760 000 bookings/day): about 13 000 searches/s at peak, so the calendar cache becomes a regional tier and channels need dedicated push clusters; booking writes (about 90/s in promotions) still fit a sharded cluster. 0.1x (120 properties): one database with a replica and a read-through cache; overbooking limits may be set per property instead of per night | Review at each doubling of properties | Search and channel push grow first | [Note] |
| [Decision] | [Options] | [Chosen] | [Why] | [Cost] |
11. Delivery plan and risks
11.1 Milestones
| Milestone | Scope | Exit criterion | Target date |
|---|---|---|---|
| M1 | Inventory model, holds and bookings for 10 pilot properties, web channel | Nightly audit clean for 30 days | YYYY-MM-DD |
| M2 | Rate plans, restrictions, calendar search | Search p95 at or below 300 ms | YYYY-MM-DD |
| M3 | PMS connector and channel push | Channel lag p99 at or below 60 s | YYYY-MM-DD |
| M4 | Overbooking limits and walk workflow | Walk rate within forecast for one season | YYYY-MM-DD |
11.2 Migration and rollback
[Example: Migrate property by property. For each property, freeze availability changes for 10 minutes, load future reservations from the old system and the PMS, reconcile counts night by night, then switch channels to the new push. Rollback restores the old system as the source and replays reservations made in the new one.]
11.3 Risks
| ID | Risk | Likelihood | Impact | Mitigation | Owner |
|---|---|---|---|---|---|
| R-01 | Example: Migration counts differ from the PMS | High | High | Night-by-night reconciliation before cutover | [@name] |
| R-02 | Example: Overbooking model ignores groups and events | Medium | High | Lower limits on flagged nights; review after each season | [@name] |
| R-03 | Example: A channel cannot handle push frequency | Medium | Medium | Per-channel batching and rate limits | [@name] |
| [R-nn] | [Risk] | [L / M / H] | [L / M / H] | [Mitigation] | [@name] |
12. Appendices
12.1 Open questions
| ID | Question | Owner | Due |
|---|---|---|---|
| OQ-01 | Example: Do we price by length of stay (different nightly price for 7-night stays)? | [@name] | YYYY-MM-DD |
| OQ-02 | Example: Should room upgrades at booking consume the higher room type's inventory? | [@name] | YYYY-MM-DD |
| [OQ-nn] | [Question] | [@name] | YYYY-MM-DD |
12.2 Glossary
| Term | Meaning |
|---|---|
| BAR | Best available rate; the base public rate from which others derive |
| ARI | Availability, rates and inventory sent to channels |
| MinLOS, MaxLOS | Minimum and maximum length of stay, set on the arrival night |
| CTA, CTD | Closed to arrival, closed to departure |
| Walk | Relocating a guest with a reservation to another hotel because none is free |
| Out of order | Rooms unavailable for sale because of maintenance |
| Group block | Rooms reserved for a group until a cutoff date |
| Look-to-book | Availability queries per booking |
| [Term] | [Meaning] |
12.3 References
| Reference | Use |
|---|---|
| Rothstein, M. "Hotel overbooking as a Markovian sequential decision process", Decision Sciences 5(3), 389 to 404, 1974, doi:10.1111/j.1540-5915.1974.tb00624.x | 7.5, 7.7 |
| Talluri, K. T. and van Ryzin, G. J. "The Theory and Practice of Revenue Management", Springer, 2004, doi:10.1007/b139000 | 7.5, 7.7 |
| Cross, R. G., Higbie, J. A. and Cross, Z. N. "Revenue Management's Renaissance", Cornell Hospitality Quarterly 50(1), 56 to 81, 2009, doi:10.1177/1938965508328716 | 7.7 |
| draft-ietf-httpapi-idempotency-key-header-08 (Internet-Draft, October 2026) and RFC 9457 Problem Details | 4.1 |
Ticket and Hotel Booking System Design (system-design/design-ticket-booking) | Seat-based inventory, waiting rooms and the simpler allotment model this design extends |
E-commerce Checkout and Inventory System Design (system-design/design-ecommerce-checkout-inventory) | Counted inventory reservations |
Payment Ledger System Design (system-design/design-payment-ledger) | Deposits, penalties and refunds |
Back-of-Envelope Estimation and Capacity Model (system-design/back-of-envelope-estimation) | Method for section 3 |
Standard and licence note
Based on common industry practice for hotel central reservation systems and on the revenue management literature cited in 12.3, paraphrased only. No standard governs this design. The overbooking table is computed from the stated example inputs with an exact binomial model; all prices, volumes and limits are illustrative examples, not benchmarks. Notes on relocation rules are not legal advice; check the current official text and a qualified professional. Original text written for this collection. Template licence: MIT.
Related templates
Ad Click Aggregation System Design Template (Reference Design)
45-minute path, click dedupe, event-time windows, exactly-once counting, exact and Count-Min top-K, batch reconciliation for billing, failure modes, 10x row
System DesignZanzibar-Style Authorization Service System Design Template
Relationship-based authorization service: relation tuples, namespace config, Check/Expand/List/Watch APIs, consistency tokens, caching, sharding, failure modes
System DesignCDC Pipeline System Design Template
CDC pipeline: log-based capture, watermark chunked snapshot, per-key order, schema evolution, outbox, search, cache and warehouse sinks, replay