Hard · reference design
Booking system for stays
Let guests search and book stays without ever selling the same night twice: fast, slightly stale search; a strictly consistent claim at the moment of booking; and payments, holds and outside calendars that never leave the inventory wrong.
1 Requirements
Functional
- Guests search available stays by place and dates.
- Guests book a stay for a range of nights and pay for it.
- A booking is held while the guest pays, then confirmed or released.
- Hosts who also list elsewhere keep a single, correct calendar.
Non-functional
- Never double-book a night: correctness over availability at the moment of booking.
- Search is fast and can be a few seconds stale.
- Retries from clients or payment providers never create a second booking or a second charge.
- Scale to millions of searches a day and thousands of bookings a minute at peak.
Out of scope: Ranking and personalization of search results. Reviews, messaging and host payouts.
2 Core entities
- Listing
- A bookable place, with its time zone and house rules.
- Night claim
- One row per listing and night: the unit that can only be sold once.
- Booking
- Guest, listing, nights, pinned price and state: held, confirmed, cancelled or expired.
- Quote
- The price the guest saw, with an expiry, frozen at booking time.
3 API
GET /listings?place=…&checkIn=…&checkOut=…Search, served from an availability index that may be slightly stale.
POST /bookingsBody: listingId, nights, quoteId, idempotencyKey. Returns 201 with a held booking, or 409 if a night is taken.
POST /bookings/{id}/confirmCalled after payment authorization; moves the booking from held to confirmed.
4 Data flow
- The guest searches; the index returns listings that look available.
- The guest books; the service claims every night atomically and creates a held booking.
- The payment is authorized; the booking moves to confirmed and events go out.
- Unpaid holds expire and release their nights; outside calendars are synced into the same claims.
5 High-level design
Press play to follow one request through the system. Every step explains what happens and the trade-off behind the choice.
Follow one guest from search to a confirmed stay, and see how the same nights stay correct when holds expire and calendars change elsewhere. Press play, or step through the stages.
6 Deep dives
How do we guarantee a night is never sold twice?
| Option | Gives | Costs |
|---|---|---|
| Check, then insert in the application | Easy to write and read. | Two requests can both pass the check before either writes. |
| Lock the listing row during booking | Correct, and simple to reason about. | Every booking for a listing waits in line, even for different dates. |
| Distributed lock in a cache | Works across services and databases. | Another system to run, and an expired lease can let two writers through. |
| Unique constraint on listing and night | The database decides atomically; different dates never block each other. | One row per night, and multi-night stays need all rows in one transaction. |
Choice. A unique constraint on listing and night, written in one transaction per booking. It is the simplest thing that is correct under concurrency.
Instant booking or request to book?
| Option | Gives | Costs |
|---|---|---|
| Instant booking | Best for guests and conversion: the stay is confirmed in seconds. | Hosts give up the chance to review each guest. |
| Request to book | Hosts approve each guest. | The hold has to last hours instead of minutes, keeping nights off the market and guests waiting. |
Choice. Support both as a listing setting. The state machine gains a "pending host approval" state with its own, longer expiry, and nothing else changes.
How do we store dates so stays are correct everywhere?
| Option | Gives | Costs |
|---|---|---|
| UTC timestamps for check-in and check-out | Standard for events and logs. | A night belongs to a local calendar day; time zones and daylight saving shift it. |
| Local dates of the listing, plus its time zone | A night is a calendar date where the property is, which is what guests and hosts mean. | Every comparison must use the listing time zone, never the server one. |
Choice. Nights are stored as local dates of the listing, with its time zone on the listing. Timestamps are kept only for events such as when a booking was made.
In one sentence
Search can be stale, but the claim cannot: one row per night under a unique constraint, held briefly while the card is authorized, confirmed through a state machine, announced through an outbox, and reconciled with every outside calendar.