. Design Travel Booking Platform (Expedia/Booking.com) - Frontend System Design Interview Guide
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
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
DiscoveryWhat 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 HaveMVP (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 BarPerformance:
- 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 │ │
│ │ NYC → PAR • Mar 15-22 • 2 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/XYZ789Benefits:
- ✅ 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 │◄───────┐
│ └───────┬───────┘ │
│ BACK │ FLIGHTS_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
- CSR vs SSR/ISR: Rendering Strategies
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 callbacksFlightResultsProps: priced itinerary options and selection actionsTravelerFormProps: traveler data + validation and save handlersCheckoutPanelProps: 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:
- URL: Current step, session ID
- Session Storage: Full booking state (survives refresh)
- 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 orderpageInfo/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
- Normalized vs Denormalized: Data Normalization
Component Boundaries
StructuredBookingFlowShell
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.
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>;
}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
StructuredSession-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
| Error | User Impact | Recovery |
|---|---|---|
| Network failure | Can't proceed | Retry button + offline indicator |
| Session expired | Lose progress | Show what was lost, restart option |
| Price changed | Unexpected cost | Confirm new price or restart |
| Sold out | Can't book | Show alternatives, back to search |
| Payment failed | Stuck | Clear error, retry with same or different method |
Performance Targets
| Metric | Target | Technique |
|---|---|---|
| Search results | < 3s | Server-side, skeleton UI |
| Step transition | < 500ms | Prefetch next step |
| Form validation | < 100ms | Client-side validation |
| Price verification | < 1s | Pre-payment API call |
| Payment processing | < 5s | Progress indicator |
Why This Design Works
StructuredURL-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
- URL-based steps enable back button, bookmarks, and analytics
- State machine ensures only valid transitions in booking flow
- Session storage preserves state across page refreshes
- Pre-payment verification handles dynamic pricing gracefully
- Progressive validation gives real-time feedback without being annoying
- Session timer creates urgency without surprise expiration
- Error recovery always gives users a clear path forward
- 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