DDSA Solutions
Case Study6 min read·

Design Airbnb (Hotel / Home Booking Marketplace)

How to design Airbnb for interviews: listing search, availability calendar, booking holds, payments, host/guest flows, and scaling a two-sided marketplace.

Airbnb is not just “Ticketmaster for houses.” You have a two-sided marketplace: hosts publish listings with calendars and prices; guests search, book, and pay; the platform sits in the middle with trust, messaging, and money movement. Interviewers use it to test search, inventory consistency, and payment flows in one design. If you already did ticket booking, reuse the hold-then-confirm idea — seats become nights.

Open with the interview framework: clarify city-level search vs map search, whether instant book exists, and how far ahead calendars go. Most interviews want search + book + payment, not the full review ML stack.

Functional requirements

  • Hosts create listings: photos, amenities, location, nightly price, availability calendar.
  • Guests search by city/dates/guests and filter (price, bedrooms, pets).
  • Guest books a stay; host may auto-accept (instant book) or approve.
  • Payment authorize on book, capture on check-in or after host accept.
  • Messaging between host and guest; reviews after stay.

Non-functional requirements

  • Search latency under ~200 ms p99 for common city queries.
  • No double-booking the same listing for overlapping nights.
  • Strong consistency on booking writes; search can be slightly stale.
  • High availability on browse; booking can fail closed rather than oversell.

Clarify inventory model

One listing = one physical unit (entire home) is the default. Multi-room hotels with identical room types need inventory counts — say which model you assume early.

Capacity sketch

Assume 5M listings, 100M searches/day (~1K QPS average, 5K peak), 500K bookings/day. Search dominates. Photos are heavy — same CDN story as Instagram. Booking QPS is modest; correctness matters more than raw throughput.

High-level architecture

  1. Listing service — CRUD for listing metadata; photos to S3 + CDN.
  2. Calendar / inventory service — night-level availability and holds.
  3. Search service — Elasticsearch or OpenSearch index of listings + geo.
  4. Booking service — create reservation, call payments, update calendar.
  5. Payment service — authorize/capture via Stripe-like PSP (payment design).
  6. Notification + messaging — booking emails, in-app chat (notifications, chat).

Data model (interview-friendly)

EntityKey fieldsStore
Listinglisting_id, host_id, lat/lng, price, amenities JSONPostgreSQL + search index
CalendarNightlisting_id, date, status (open/held/booked)SQL or Redis for hot dates
Bookingbooking_id, guest_id, listing_id, check_in, check_out, statusPostgreSQL
Paymentbooking_id, auth_id, amount, statusLedger / payment DB

Avoid one row per night forever without pruning — archive past nights. For “next 18 months” availability, materialize nights or store ranges with exceptions. Ranges are compact; night rows make conflict checks trivial. Pick one and defend it.

Search path

Guest query: city=Paris, check_in, check_out, guests=2. Search service queries geo + filters in Elasticsearch, then filters candidates that have open nights for the date range. Do not run calendar joins on every listing in Postgres at peak — denormalize a “available_from / available_to” hint or maintain a secondary availability index updated on calendar writes.

  • Geo: geohash or geo_point queries for map viewport.
  • Ranking: price, review score, host response rate, distance — keep ranking simple in interviews.
  • Cache hot city landing pages in Redis/CDN for anonymous browse.
  • Eventual consistency: new listing appears in search within seconds via async indexer (Kafka).

Booking and double-booking prevention

  1. Guest clicks Book → booking service starts a transaction or saga.
  2. Try to mark nights [check_in, check_out) as `held` with TTL (e.g. 10 minutes) for this booking_id.
  3. If any night already held/booked → fail with conflict.
  4. Authorize payment; on success flip nights to `booked` and booking to `confirmed`.
  5. On timeout or payment fail → release hold (TTL expiry job or explicit cancel).

Same pattern as ticket booking: optimistic hold + payment + confirm. Use `SELECT … FOR UPDATE` on night rows, or a conditional update `WHERE status = open`. Redis SETNX per `listing:date` key works for hot listings if you accept Redis as source of truth for holds and sync to SQL.

CAP choice

For inventory, prefer consistency over availability — better to show “unavailable” than double-book. Search can be AP. See CAP theorem.

Instant book vs host approval

Instant book: hold → pay → confirm in one flow. Request-to-book: create `pending_host` booking, notify host, hold nights with longer TTL (24h). Host accept triggers capture; decline releases hold and voids auth. State machine on booking status keeps the story clear on the whiteboard.

Photos and media

Presigned upload to S3, async resize, CDN URLs on listing — copy the media pipeline from file storage / Instagram. Listing pages are read-heavy; cache HTML fragments or JSON for popular listings.

Scaling checklist

  • Shard bookings by listing_id or booking_id (sharding).
  • Search cluster separate from OLTP — never let Elasticsearch be the booking source of truth.
  • Rate-limit scrapers on search (rate limiter, API gateway).
  • Idempotency keys on CreateBooking so retries do not double-charge.

Worked example

  1. Guest searches Paris, Jun 10–12, 2 guests → ES returns listing L42.
  2. Calendar shows Jun 10 and 11 open.
  3. Book: hold both nights for booking B99, TTL 10 min.
  4. Payment auth succeeds → nights booked, B99 confirmed, host notified.
  5. Concurrent book on same nights fails at hold step — guest sees “dates taken.”

Pricing and dynamic rates

MVP: fixed nightly price on the listing. Production: weekend premiums, seasonal rules, length-of-stay discounts. Keep a pricing service that returns a quote for (listing, dates) at book time and freeze that quote on the booking row — never re-price after payment auth. Dynamic pricing ML is optional depth; freeze-the-quote is the interview-safe rule.

Trust and safety (short)

  • ID verification and host/guest reviews after checkout.
  • Messaging stays on-platform to detect scams (chat).
  • Payouts to hosts on a delayed schedule after guest check-in (chargebacks).
  • Fraud signals on new accounts — rate-limit listing creation.

Multi-region: pin booking writes to the listing’s home region to avoid split-brain calendars; replicate listings read-only elsewhere for search. Guests booking across regions accept slightly higher latency on confirm.

Cancellations and refunds

Cancellation policy (flexible/moderate/strict) is metadata on the listing. Cancel transitions booking to `cancelled`, frees nights, and calls payment refund/void APIs with the same idempotency discipline as capture. Partial refunds for early checkout are ledger entries — point back to the payment system article rather than inventing accounting on the whiteboard.

Interview narrative

Spend time on search vs booking consistency, the hold TTL, and payment authorize/capture. Mention marketplace trust (reviews, messaging) as secondary. Compare to pure e-commerce: inventory is time-ranged, not SKU counts. That distinction shows you understand the problem.

More in this series