Designing a flight booking system
Search flights across airlines and book seats without double-selling — complex multi-leg search, volatile inventory/pricing, and a hold-and-confirm reservation.
The problem
Design a flight booking system (an OTA like Expedia, or an airline’s): search flights by route/date across many airlines, show fares, and book a seat without double-selling it. The challenges: complex search (multi-leg itineraries), volatile third-party inventory/pricing, and a consistent reservation that can’t oversell.
Step 1 — Requirements
Functional: search flights (origin, destination, dates, passengers; one-way/round/ multi-city); show fares/availability; select + book seats; payment; manage bookings (PNR); cancellations.
Non-functional: low-latency search over a huge combination space, fresh availability/pricing (changes constantly), strong consistency for booking (no double-sold seats), available, scalable (read-heavy search ≫ bookings).
Step 2 — Search (the hard read problem)
Flight search explodes combinatorially — multi-leg itineraries, connections, date flexibility, multiple airlines:
- Inventory source — airlines/GDS (Global Distribution Systems like Amadeus/ Sabre) hold the real availability + fares. An OTA queries them (slow, rate-limited, paid per query).
- Caching — cache fare/availability results aggressively (with short TTLs, since they change) to avoid hammering the GDS on every search; pre-cache popular routes.
- Itinerary construction — build connecting itineraries (graph search over flights/ airports within time constraints) and rank by price/duration/stops.
So search = cached GDS data + itinerary assembly + ranking. It’s read-heavy and latency-sensitive.
Step 3 — The freshness problem
Cached availability/price may be stale by booking time (someone else booked, fare changed). So:
- Re-validate at booking — re-query the airline/GDS for the exact fare + seat right before reserving; the cached search result is a candidate, not a guarantee.
- Show a clear “price/availability confirmed at checkout” UX — fares can change between search and book.
Step 4 — Booking without overselling (consistency core)
Reserve seats with a hold-and-confirm flow against the airline’s inventory (the seat count is a scarce, strongly-consistent resource):
select itinerary → re-validate fare/availability → HOLD seats (temporary lock, minutes)
→ take payment → CONFIRM → issue PNR/ticket (payment fails / hold expires → release)
The hold (lease) reserves seats during checkout; the airline’s inventory system (or your mirror with a unique constraint per seat) enforces no-double-sell — the Airbnb pattern, across a third-party. Booking is CP.
Step 5 — Architecture
search → cache (fares/availability) ⇄ GDS/airline APIs → itinerary builder → ranked results
booking → re-validate → hold (lease) → payment → confirm (PNR) → ticketing
manage → PNR store; cancellations/refunds (saga with the airline)
Step 6 — PNR and post-booking
A confirmed booking is a PNR (Passenger Name Record) stored durably; changes/cancellations are a saga coordinating with the airline (refund + release seats), idempotent and reconciled (payment lessons).
Trade-offs to raise
- Cache search (fast, may be stale) vs live GDS per search (fresh, slow/costly). Cache with short TTLs + re-validate at booking.
- Hold/lease (good UX, seats briefly locked) vs book-on-click.
- Search latency vs coverage — more airlines/combinations = richer but slower; parallel fan-out + caching.
- CP booking over availability.
The interview cue
“Search assembles itineraries from cached GDS availability/fares (short TTLs, pre-cache hot routes) and ranks them; because fares/seats are volatile, re-validate at booking and use a hold → pay → confirm flow against the airline’s inventory (no double-sell — CP), issuing a PNR; cancellations are a saga with the airline.” Cached complex search + freshness re-validation + CP hold-and-confirm is the answer; implementation next.