. Design Travel Booking Platform (Expedia/Booking.com) - Frontend System Design Interview Guide

Hard

Design a production-ready travel booking platform like Expedia, Booking.com, or Kayak with flight/hotel search, multi-step booking flows, real-time pricing, and payment integration.

Backend as Black Box: Assume you have APIs for search, pricing, and booking. Focus on the frontend architecture.

Key Challenges

This problem explores complex e-commerce flows:

  • Multi-Step Booking Wizard: Search → Select → Details → Payment → Confirmation
  • Real-time Pricing: Dynamic prices that change during booking
  • Session & Cart Management: Hold inventory, handle timeouts
  • Form Orchestration: Complex validation across multiple steps
  • Payment Integration: Secure checkout, error handling
Quick Links:

When users book travel, they expect smooth multi-step flows, accurate pricing, and reliable payment processing—even as prices change in real-time. This solution designs a travel booking platform that handles multi-step wizards, real-time pricing, session management, and payment integration while gracefully managing price volatility. The key insight: a good booking platform re-validates prices at confirmation time, handles session timeouts gracefully, and preserves state across refreshes.

HLD interview focus: Requirements, architecture, tradeoffs, data flow, and scaling decisions. Any implementation snippets shown are optional unless explicitly asked.

I'll start by defining what makes a great booking experience—what questions does a user need answered as they search and book? Then I'll design the architecture that handles multi-step wizards, real-time pricing, session management, and payment integration. Finally, I'll design clear boundaries between booking state and UI state.

Why this approach?

Most candidates build a "form with payment." Strong candidates build a "booking platform"—a system that handles price volatility, session timeouts, multi-step validation, and graceful error recovery. The difference is thinking about the entire booking journey, not just individual steps.

Think of this like building an e-commerce checkout. You don't just collect payment—you handle inventory holds, price verification, session management, and error recovery. Same principles apply here.

Before designing anything, let's define what success looks like. When users book travel, they need smooth flows, accurate pricing, and reliable payment processing.

Requirements Exploration Questions

Discovery
What booking types?
  • Flights (one-way, round-trip, multi-city)
  • Hotels (date range, room types)
  • Packages (flight + hotel bundles)
  • Car rentals, activities
What's the booking flow?
  • Search → Select → Details → Extras → Payment → Confirmation
  • How many steps? (Typically 4-6)
  • Can users go back and modify?
How do prices work?
  • Real-time pricing (can change during booking)
  • How long are prices held? (15-30 minutes typical)
  • What happens when price changes?
What payment methods?
  • Credit/debit cards
  • PayPal, Apple Pay, Google Pay
  • Buy now, pay later options

Functional Requirements

Must Have

MVP (Core Features - What I'd Design First):

  • Search flights/hotels with dates and passengers
  • Multi-step booking wizard with progress indicator
  • Real-time price updates and verification
  • Session management with timeout handling (15-30 min holds)
  • Passenger details with validation
  • Payment integration and confirmation
  • Clear loading/empty/error states
  • Basic accessibility and keyboard support

Advanced Features (Add If Time Permits):

  • Multi-city/multi-leg journeys
  • Seat/room selection and add-ons
  • Resume interrupted bookings
  • Offline draft queue for incomplete bookings

Non-Functional Requirements

Quality Bar

Performance:

  • Search results in < 3 seconds
  • Step transitions < 500ms
  • Form validation in real-time
  • Mobile-first responsive design

Reliability:

  • Handle network failures gracefully
  • Preserve state across refreshes
  • Clear error messages with recovery options
  • No lost bookings

Security:

  • PCI compliance for payments
  • Secure form handling
  • CSRF protection

Scalability:

  • Handle peak traffic with stable latency
  • Keep memory bounded with virtualization/windowing
  • Support efficient pagination/indexing at large dataset sizes

Accessibility:

  • WCAG 2.1 AA baseline
  • Full keyboard navigation for core flows
  • Screen reader-friendly labels, roles, and announcements

Observability:

  • Track p95 latency, error rate, and retry rate
  • Log critical client/server sync failures
  • Alert on sustained degradation and queue backlog growth

Booking Flow as State Machine

┌─────────────────────────────────────────────────────────────────────────────┐
TRAVEL BOOKING JOURNEY├─────────────────────────────────────────────────────────────────────────────┤
│                                                                              │
│  ┌──────────┐   ┌──────────┐   ┌──────────┐   ┌──────────┐   ┌──────────┐  │
│  │  SEARCH  │──►│  SELECT  │──►│ DETAILS  │──►│ PAYMENT  │──►│ CONFIRM  │  │
│  │          │   │          │   │          │   │          │   │          │  │
│  │ Where?   │   │ Which    │   │ Who's    │   │ How to   │   │ Booking  │  │
│  │ When?    │   │ flight?  │   │ flying?  │   │ pay?     │   │ complete │  │
│  │ Who?     │   │          │   │          │   │          │   │          │  │
│  └──────────┘   └──────────┘   └──────────┘   └──────────┘   └──────────┘  │
│       │              │              │              │              │         │
│       ▼              ▼              ▼              ▼              ▼         │
│  ┌──────────────────────────────────────────────────────────────────────┐  │
│  │                     PERSISTENT STATE BAR                             │  │
│  │  NYCPAR  •  Mar 15-222 Adults  •  $1,247[Continue →]   │  │
│  └──────────────────────────────────────────────────────────────────────┘  │
│                                                                              │
│  Cross-cutting concerns:│  • Session timeout (15 min warning, 30 min expiry)│  • Price change notifications                                                │
│  • Inventory availability checks                                             │
│  • Error recovery at any step                                               │
│                                                                              │
└─────────────────────────────────────────────────────────────────────────────┘

Why URL-Based Step Navigation?

URL Structure:

/flights/search?from=NYC&to=PAR&depart=2024-03-15&return=2024-03-22&pax=2
/flights/results?sessionId=abc123
/flights/book/abc123/passengers
/flights/book/abc123/extras
/flights/book/abc123/payment
/flights/confirmation/XYZ789

Benefits:

  • Back button works naturally - Browser navigation just works
  • Steps are bookmarkable - User can save progress
  • Easy to share - Send link to specific step
  • Analytics built-in - Track funnel drop-off by URL
  • Deep linking for support - Customer service can see exact step

State Machine for Valid Transitions

┌───────────────┐
SEARCH                    └───────┬───────┘
SEARCH_SUBMITTED
                    ┌───────────────┐
           ┌───────►│    RESULTS    │◄───────┐
           │        └───────┬───────┘        │
BACKFLIGHTS_SELECTED
           │                ▼                │
           │        ┌───────────────┐        │
           ├───────►│  PASSENGERS   │◄───────┤ BACK
           │        └───────┬───────┘        │
           │                │ PASSENGERS_SUBMITTED
           │                ▼                │
           │        ┌───────────────┐        │
           ├───────►│    EXTRAS     │◄───────┤
           │        └───────┬───────┘        │
           │                │ CONTINUE
           │                ▼
           │        ┌───────────────┐
           │        │    PAYMENT           │        └───────┬───────┘
           │                │ PAYMENT_SUCCESS
           │                ▼
           │        ┌───────────────┐
           └────────│ CONFIRMATION   (final state)
                    └───────────────┘

Global transitions:
- SESSION_EXPIRED → Expired state
- SOLD_OUT → Sold out state
- PAYMENT_FAILED → Payment state (with error)

Why State Machine?

  • Only valid transitions allowed
  • Clear handling of edge cases
  • Easy to test each transition
  • Self-documenting flow

Tradeoffs & Comparisons

Entity and interface contract shape with cache/reconciliation model for a frontend system design interview (backend treated as a black box).

1) Component prop interfaces (boundaries)

Define explicit boundaries across search, selection, traveler details, and checkout flows.

  • BookingFlowShellProps: booking session context + step navigation callbacks
  • FlightResultsProps: priced itinerary options and selection actions
  • TravelerFormProps: traveler data + validation and save handlers
  • CheckoutPanelProps: payment summary + confirm action

2) Hook interfaces (consumption contracts)

Hooks expose step-wise booking state and mutation contracts without transport details.

Core Data Structures

Search Parameters:

interface FlightSearch {
  tripType: 'roundtrip' | 'oneway' | 'multicity';
  legs: TripLeg[];
  passengers: {
    adults: number;
    children: number;
    infants: number;
  };
  cabinClass: 'economy' | 'premium_economy' | 'business' | 'first';
}

interface TripLeg {
  origin: Airport;
  destination: Airport;
  departDate: Date;
  returnDate?: Date;
}

Booking Session:

interface BookingSession {
  id: string;
  createdAt: Date;
  expiresAt: Date;              // 30 min from creation
  
  // Selected flights
  selectedFlights: Flight[];
  
  // Current price (can change!)
  pricing: {
    basePrice: number;
    taxes: number;
    fees: number;
    total: number;
    currency: string;
    quotedAt: Date;             // When price was quoted
  };
  
  // Passenger details
  passengers: PassengerDetails[];
  
  // Extras
  extras: {
    baggage?: BaggageSelection;
    seats?: SeatSelection[];
    insurance?: InsuranceOption;
  };
  
  // Contact
  contactInfo: {
    email: string;
    phone: string;
  };
}

Passenger Details (with validation):

interface PassengerDetails {
  type: 'adult' | 'child' | 'infant';
  title: 'mr' | 'mrs' | 'ms' | 'dr';
  firstName: string;           // As on passport
  lastName: string;
  dateOfBirth: Date;
  gender: 'male' | 'female';
  nationality: string;
  passport?: {
    number: string;
    country: string;
    expiryDate: Date;          // Must be 6 months after travel
  };
}

Session Storage Strategy

What to persist:

interface PersistedBookingState {
  sessionId: string;
  searchParams: FlightSearch;
  selectedFlights: Flight[];
  passengers: PassengerDetails[];
  extras: ExtraSelections;
  currentStep: BookingStep;
  lastUpdated: Date;
}

Storage layers:

  1. URL: Current step, session ID
  2. Session Storage: Full booking state (survives refresh)
  3. Server: Canonical state (source of truth)

Why Session Storage?

  • Survives page refresh
  • Cleared when tab closes (security)
  • Fast local access
  • Reduces server round-trips

Client cache shape (recommended)

  • entitiesById: Record<ID, Entity>
  • orderedIds: ID[] for rendering order
  • pageInfo/cursor metadata for pagination or range loading

Deep dive: Data Normalization

Consistency & reconciliation rules

  • Make writes idempotent where retries are possible.
  • Apply realtime updates with version/event ordering checks.
  • Prefer server-authoritative reconciliation after optimistic mutations.

Tradeoffs & Comparisons

Component Boundaries

Structured
BookingFlowShell

Coordinates multi-step navigation and booking session lifecycle.

FlightResults

Renders itinerary options and emits select/sort/filter actions.

TravelerForm

Captures traveler/contact details with validation hooks.

CheckoutPanel

Shows final price, payment method selection, and confirmation intent.

travel-booking-component-interfaces.ts
export interface BookingFlowShellProps {
  sessionId?: string;
  currentStep: BookingStep;
  onStepChange: (step: BookingStep) => void;
}

export interface FlightResultsProps {
  itineraries: ItinerarySummary[];
  selectedItineraryId?: string;
  onSelectItinerary: (itineraryId: string) => void;
  onApplyFilter: (filter: ResultsFilter) => void;
}

export interface TravelerFormProps {
  travelers: TravelerDraft[];
  onChangeTraveler: (index: number, patch: Partial<TravelerDraft>) => void;
  onSaveTravelers: () => Promise<void>;
}

export interface CheckoutPanelProps {
  pricing: BookingPrice;
  canConfirm: boolean;
  onConfirmBooking: () => Promise<void>;
}
travel-booking-hook-contracts.ts
export interface UseBookingSessionResult {
  session: BookingSession | null;
  currentStep: BookingStep;
  setStep: (step: BookingStep) => void;
  isLoading: boolean;
}

export interface UseBookingMutationsResult {
  selectItinerary: (itineraryId: string) => Promise<void>;
  saveTravelers: (travelers: TravelerDraft[]) => Promise<void>;
  confirmBooking: () => Promise<void>;
  pendingCount: number;
}

export function useBookingSession(_sessionId?: string): UseBookingSessionResult {
  throw new Error('Contract-only snippet');
}

export function useBookingMutations(_sessionId: string): UseBookingMutationsResult {
  throw new Error('Contract-only snippet');
}

React interfaces & integration patterns (props, hooks, callbacks).

This section covers API contracts and React consumption patterns.

API contracts (Backend as black box)

Flight Search API:

GET /api/flights/search?origin={code}&destination={code}&departureDate={date}&returnDate={date}&passengers={count}&cabinClass={class}

Request Parameters:
- origin: Airport code (required)
- destination: Airport code (required)
- departureDate: ISO date string (required)
- returnDate: ISO date string (optional, for round-trip)
- passengers: Number of passengers (default: 1)
- cabinClass: "economy" | "premium" | "business" | "first" (default: "economy")

Response:
{
  flights: Flight[];
  searchId: string;              // For session tracking
  expiresAt: string;              // ISO timestamp
}

interface Flight {
  id: string;
  airline: string;
  flightNumber: string;
  departure: { airport: string; time: string; };
  arrival: { airport: string; time: string; };
  duration: number;               // minutes
  stops: number;
  price: number;
  currency: string;
  availableSeats: number;
  aircraft: string;
}

Booking Session API:

POST /api/bookings/sessions
{
  searchId: string;
  selectedFlights: string[];      // Flight IDs
}

Response:
{
  sessionId: string;
  expiresAt: string;              // ISO timestamp (typically 15-20 minutes)
  totalPrice: number;
  currency: string;
}

GET /api/bookings/sessions/:sessionId
Response: { session: BookingSession; }

PUT /api/bookings/sessions/:sessionId/extend
Response: { expiresAt: string; }

Passenger & Booking API:

POST /api/bookings/sessions/:sessionId/passengers
{
  passengers: PassengerDetails[];
  contactInfo: ContactInfo;
}

Response: { success: boolean; }

POST /api/bookings/sessions/:sessionId/extras
{
  baggage: BaggageSelection[];
  seats: SeatSelection[];
  insurance: boolean;
}

Response: { 
  success: boolean;
  updatedPrice: number;
  priceChanged: boolean;          // If price changed since selection
}

Payment & Confirmation API:

POST /api/bookings/sessions/:sessionId/payment
{
  paymentMethodId: string;        // Stripe payment method ID
  billingAddress: Address;
}

Response:
{
  bookingId: string;
  confirmationCode: string;
  itinerary: Itinerary;
}

GET /api/bookings/:bookingId
Response: { booking: BookingDetails; }

Price Verification API:

POST /api/bookings/sessions/:sessionId/verify-price
Response:
{
  currentPrice: number;
  originalPrice: number;
  priceChanged: boolean;
  priceDifference?: number;
}

Type definitions used in contracts

interface Flight {
  id: string;
  airline: string;
  flightNumber: string;
  departure: { airport: string; time: string };
  arrival: { airport: string; time: string };
  price: number;
  currency: string;
}

interface BookingSession {
  sessionId: string;
  expiresAt: string;
  totalPrice: number;
  currency: string;
}

interface BookingDetails {
  bookingId: string;
  confirmationCode: string;
  status: 'confirmed' | 'pending' | 'failed';
}

3) Integration patterns (React wiring)

  • Step-wise orchestration: each step reads normalized booking session state.
  • Price revalidation UX: surface fare changes before payment commit.
  • Optimistic step transitions: move user forward with clear pending/rollback states.
  • Resume-safe sessions: restore local draft and reconcile with server session.

Integration Patterns

Structured
Session-first state

Drive UI from booking session snapshot across all steps.

Fare-change handling

Require explicit re-accept flow when price verification changes total.

Optimistic progression

Advance UI with pending state while persisting server updates.

Recovery after refresh

Hydrate local step state and reconcile with active server session.

Error Recovery Patterns
ErrorUser ImpactRecovery
Network failureCan't proceedRetry button + offline indicator
Session expiredLose progressShow what was lost, restart option
Price changedUnexpected costConfirm new price or restart
Sold outCan't bookShow alternatives, back to search
Payment failedStuckClear error, retry with same or different method
Performance Targets
MetricTargetTechnique
Search results< 3sServer-side, skeleton UI
Step transition< 500msPrefetch next step
Form validation< 100msClient-side validation
Price verification< 1sPre-payment API call
Payment processing< 5sProgress indicator

Why This Design Works

Structured
URL-Based Step Navigation
  • Back button works - Natural browser behavior
  • Steps are bookmarkable - Resume interrupted bookings
  • Easy analytics - Track funnel by URL
  • Deep linking - Support can see exact step
State Machine for Booking Flow
  • Only valid transitions - Can't skip steps
  • Clear edge cases - Timeout, sold out, payment failed
  • Easy testing - Test each transition
  • Self-documenting - Flow is explicit
Session Storage for Resilience
  • Survives refresh - Don't lose progress
  • Auto-clears - Secure (clears on tab close)
  • Fast access - No server round-trip
  • Fallback - Server is still source of truth
Pre-Payment Price Verification
  • No surprises - Confirm price before charging
  • User consent - Accept or decline new price
  • Clear communication - Show exactly what changed

Key Takeaways

  1. URL-based steps enable back button, bookmarks, and analytics
  2. State machine ensures only valid transitions in booking flow
  3. Session storage preserves state across page refreshes
  4. Pre-payment verification handles dynamic pricing gracefully
  5. Progressive validation gives real-time feedback without being annoying
  6. Session timer creates urgency without surprise expiration
  7. Error recovery always gives users a clear path forward
  8. Mobile-first forms with proper input types and touch targets

Key Takeaways

  • Use URL-based step navigation for back button and bookmarks
  • Model booking flow as state machine for valid transitions
  • Session storage provides resilience across page refreshes
  • Pre-payment price verification prevents checkout surprises
  • Session timer creates urgency with clear countdown
  • Progressive form validation gives real-time feedback
  • Error recovery should preserve user progress when possible
  • Mobile-first forms with proper input types and touch targets