← All designs

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 /bookings

Body: listingId, nights, quoteId, idempotencyKey. Returns 201 with a held booking, or 409 if a night is taken.

POST /bookings/{id}/confirm

Called after payment authorization; moves the booking from held to confirmed.

4 Data flow

  1. The guest searches; the index returns listings that look available.
  2. The guest books; the service claims every night atomically and creates a held booking.
  3. The payment is authorized; the booking moves to confirmed and events go out.
  4. 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.

async update
GuestSearch, then book
Search indexAvailability cache
Booking APIIdempotency key
Channel syncOther calendars
Night claimsUnique per night
Hold expiryReleases stale holds
PaymentsAuthorize first
Booking stateheld → confirmed
EventsVia an outbox

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?

OptionGivesCosts
Check, then insert in the applicationEasy to write and read.Two requests can both pass the check before either writes.
Lock the listing row during bookingCorrect, and simple to reason about.Every booking for a listing waits in line, even for different dates.
Distributed lock in a cacheWorks across services and databases.Another system to run, and an expired lease can let two writers through.
Unique constraint on listing and nightThe 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?

OptionGivesCosts
Instant bookingBest for guests and conversion: the stay is confirmed in seconds.Hosts give up the chance to review each guest.
Request to bookHosts 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?

OptionGivesCosts
UTC timestamps for check-in and check-outStandard 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 zoneA 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.