Chaturmind
LearnDSASystem DesignInterview PrepDevOpsEngineering GrowthBlog
Start learning
Chaturmind

Structured learning paths for engineers who want to go deep. Written by practitioners.

Learn

  • Java
  • DSA
  • System Design
  • Spring Boot
  • AI / ML
  • DevOps
  • Engineering Growth
  • Java Interview Prep

Company

  • About
  • Blog
  • Contact

Legal

  • Privacy Policy
  • Terms of Service

© 2026 Chaturmind. All rights reserved.

👥10,051visitors

Built for engineers who want to go deep.


← Low-Level Design & Design Patterns

OOP Fundamentals & SOLID Principles

  • Object-Oriented Design Refresher
  • Single Responsibility & Open/Closed
  • Liskov Substitution & Interface Segregation
  • Dependency Inversion

Creational Design Patterns

  • Singleton Pattern
  • Factory & Abstract Factory
  • Builder Pattern
  • Prototype Pattern

Structural Design Patterns

  • Adapter & Facade
  • Decorator Pattern
  • Composite & Proxy
  • Bridge & Flyweight

Behavioral Design Patterns

  • Strategy Pattern
  • Observer Pattern
  • Command Pattern
  • Template Method
  • State Pattern
  • Chain of Responsibility
  • Iterator & Mediator
  • Visitor Pattern

Machine Coding Exercises

  • Parking Lot — Requirements & Class Design
  • Parking Lot — Implementation
  • Elevator System — Requirements & Class Design
  • Elevator System — Implementation & Scheduling
  • Library Management System
  • Tic-Tac-Toe / Board Game Design
  • Chess Engine Design
  • Vending Machine Design
  • ATM System Design
  • In-Memory Rate Limiter
  • Thread-Safe LRU Cache
  • Connection Pool Design
  • Producer-Consumer Class Design
  • Movie Ticket Booking System
  • Hotel Reservation System
  • Airline Booking / Seat Selection System
  • Splitwise / Expense Sharing System
  • Design a Logging Framework
  • Design a Notification/Observer-Based Pub-Sub
  • Design a Cache with Pluggable Eviction Policy
  • Design Snake and Ladder
  • Design a Card Game Framework
  • Design an Inventory Management System
  • Restaurant Management System
  • Food Delivery Order Matching
  • Design a Shopping Cart & Checkout Flow
  • Design a Recommendation Engine
  • Design a Job Scheduler
  • Design a Circuit Breaker
  • Design a Distributed ID Generator
  • Design a Pub-Sub Message Broker
  • Design Twitter/X
  • Multi-Pattern Problem: Design a Ride Booking Class Model
  • Multi-Pattern Problem: Design a Food Ordering Class Model
  • Design an Authentication/Authorization Module
  • Design a Form/Survey Builder
  • Design a Meeting Room / Calendar Booking System
  • Design a Search/Filter Engine for an E-Commerce Catalog
  • Design a Voting/Polling System
  • Design a Retry Mechanism with Backoff
  • Design a Health Check Aggregator
  • Design a Feature Flag System
  • Design a Config Management Client
  • Design a Leader Election Algorithm
  • Design a Distributed Counter
  • Design a Bloom Filter
  • Design a Consistent Hashing Ring
  • Design a Distributed Tracing Library
  • Design a Metrics Collection Library
  • Design an Audit Logging Framework
  • Design a Plugin/Extension System
  • Design a Workflow Engine
  • Multi-Pattern Capstone: Design a Food Delivery App
Chaturmind
← Low-Level Design & Design Patterns

OOP Fundamentals & SOLID Principles

  • Object-Oriented Design Refresher
  • Single Responsibility & Open/Closed
  • Liskov Substitution & Interface Segregation
  • Dependency Inversion

Creational Design Patterns

  • Singleton Pattern
  • Factory & Abstract Factory
  • Builder Pattern
  • Prototype Pattern

Structural Design Patterns

  • Adapter & Facade
  • Decorator Pattern
  • Composite & Proxy
  • Bridge & Flyweight

Behavioral Design Patterns

  • Strategy Pattern
  • Observer Pattern
  • Command Pattern
  • Template Method
  • State Pattern
  • Chain of Responsibility
  • Iterator & Mediator
  • Visitor Pattern

Machine Coding Exercises

  • Parking Lot — Requirements & Class Design
  • Parking Lot — Implementation
  • Elevator System — Requirements & Class Design
  • Elevator System — Implementation & Scheduling
  • Library Management System
  • Tic-Tac-Toe / Board Game Design
  • Chess Engine Design
  • Vending Machine Design
  • ATM System Design
  • In-Memory Rate Limiter
  • Thread-Safe LRU Cache
  • Connection Pool Design
  • Producer-Consumer Class Design
  • Movie Ticket Booking System
  • Hotel Reservation System
  • Airline Booking / Seat Selection System
  • Splitwise / Expense Sharing System
  • Design a Logging Framework
  • Design a Notification/Observer-Based Pub-Sub
  • Design a Cache with Pluggable Eviction Policy
  • Design Snake and Ladder
  • Design a Card Game Framework
  • Design an Inventory Management System
  • Restaurant Management System
  • Food Delivery Order Matching
  • Design a Shopping Cart & Checkout Flow
  • Design a Recommendation Engine
  • Design a Job Scheduler
  • Design a Circuit Breaker
  • Design a Distributed ID Generator
  • Design a Pub-Sub Message Broker
  • Design Twitter/X
  • Multi-Pattern Problem: Design a Ride Booking Class Model
  • Multi-Pattern Problem: Design a Food Ordering Class Model
  • Design an Authentication/Authorization Module
  • Design a Form/Survey Builder
  • Design a Meeting Room / Calendar Booking System
  • Design a Search/Filter Engine for an E-Commerce Catalog
  • Design a Voting/Polling System
  • Design a Retry Mechanism with Backoff
  • Design a Health Check Aggregator
  • Design a Feature Flag System
  • Design a Config Management Client
  • Design a Leader Election Algorithm
  • Design a Distributed Counter
  • Design a Bloom Filter
  • Design a Consistent Hashing Ring
  • Design a Distributed Tracing Library
  • Design a Metrics Collection Library
  • Design an Audit Logging Framework
  • Design a Plugin/Extension System
  • Design a Workflow Engine
  • Multi-Pattern Capstone: Design a Food Delivery App
HomeLearnSystem DesignLow-Level Design & Design PatternsMachine Coding Exercises
✓ FreeIntermediate· 7 min read

Airline Booking / Seat Selection System

Flight/Seat/Booking/FareClass classes reusing Movie Ticket Booking's seat-map and hold-expiry patterns, plus the class-design extension needed for multi-leg itineraries.

Published September 23, 2026


Airline Booking / Seat Selection System

Core classes

class Flight { String flightNumber; Airport origin, destination; Instant departure; List<Seat> seats; }
class FareClass { String name; double priceMultiplier; } // Economy, Premium, Business, First
class Seat { String seatNumber; FareClass fareClass; }
class Passenger { String name; String passportNumber; }
class Booking { List<Flight> legs; Map<Flight, Seat> seatAssignments; Passenger passenger; BookingStatus status; }

Advertisement

Reusing the seat-map and locking pattern

Seat selection and the double-booking risk here are structurally identical to Movie Ticket Booking System — seats are per-flight (not a permanent property of Seat), and selection needs the same temporary-hold-with-expiry pattern (a passenger selecting seat 14C shouldn't permanently lock it away if they abandon checkout). Recognizing this as the same underlying problem, not a new one, is worth stating explicitly in an interview — reapplying an already-designed solution is a stronger signal than re-deriving seat-locking from scratch a second time.

Extending to multi-leg itineraries

class Booking {
    List<Flight> legs; // e.g. [JFK->LHR, LHR->CDG] for a connecting itinerary
    Map<Flight, Seat> seatAssignments; // one seat assignment PER LEG — a passenger picks a different seat on each flight
}

The key design change: Booking holds a list of flights (legs), not a single Flight, and seat assignment becomes a map keyed by leg, since a passenger's seat on the first flight of a connection has nothing to do with their seat on the second. The seat-hold-and-confirm flow from a single-leg booking applies per leg independently — holding seat 14C on leg 1 and seat 22A on leg 2 are two separate hold operations, and a booking should only fully confirm once every leg's hold succeeds (an all-or-nothing multi-leg commit, conceptually similar to the atomicity concern Two-Phase Commit discusses at the distributed-systems level, just within a single booking transaction here).

Implementing the seat hold with expiry

The per-flight seat inventory is where concurrency bugs live, so it deserves real code. Keep the state of each seat on that flight in a FlightSeatMap, not on Seat, because the same physical seat is sold separately on every departure:

enum SeatState { AVAILABLE, HELD, BOOKED }

class SeatHold {
    final String holdId; final String seatNumber; final String bookingId; final Instant expiresAt;
    SeatHold(String holdId, String seatNumber, String bookingId, Instant expiresAt) {
        this.holdId = holdId; this.seatNumber = seatNumber; this.bookingId = bookingId; this.expiresAt = expiresAt;
    }
}

class FlightSeatMap {
    private final Map<String, SeatState> states = new HashMap<>();
    private final Map<String, SeatHold> holds = new HashMap<>();
    private final Clock clock;
    private final Duration holdDuration = Duration.ofMinutes(10);

    FlightSeatMap(Collection<Seat> seats, Clock clock) {
        this.clock = clock;
        seats.forEach(s -> states.put(s.seatNumber, SeatState.AVAILABLE));
    }

    synchronized Optional<SeatHold> hold(String seatNumber, String bookingId) {
        releaseExpired();
        if (states.get(seatNumber) != SeatState.AVAILABLE) return Optional.empty();
        SeatHold hold = new SeatHold(UUID.randomUUID().toString(), seatNumber, bookingId,
                                     clock.instant().plus(holdDuration));
        states.put(seatNumber, SeatState.HELD);
        holds.put(seatNumber, hold);
        return Optional.of(hold);
    }

    synchronized boolean confirm(SeatHold hold) {
        releaseExpired();
        SeatHold current = holds.get(hold.seatNumber);
        if (current == null || !current.holdId.equals(hold.holdId)) return false;   // expired or taken over
        states.put(hold.seatNumber, SeatState.BOOKED);
        holds.remove(hold.seatNumber);
        return true;
    }

    synchronized void release(SeatHold hold) {
        SeatHold current = holds.get(hold.seatNumber);
        if (current != null && current.holdId.equals(hold.holdId)) {
            holds.remove(hold.seatNumber);
            states.put(hold.seatNumber, SeatState.AVAILABLE);
        }
    }

    private void releaseExpired() {
        Instant now = clock.instant();
        holds.values().removeIf(h -> {
            boolean expired = !h.expiresAt.isAfter(now);
            if (expired) states.put(h.seatNumber, SeatState.AVAILABLE);
            return expired;
        });
    }
}

Design points to call out:

  • Expiry is checked lazily, on every operation, so there's no background thread to get wrong. A scheduled sweeper can be added for tidiness, but correctness doesn't depend on it.
  • confirm compares hold IDs, not just seat numbers. If a hold expired and someone else re-held the seat, the first customer's late confirmation fails instead of stealing the seat.
  • Clock is injected, so tests can advance time and verify expiry without sleeping.
  • In a multi-server system, synchronized becomes a database row update with a condition (UPDATE seat SET state='HELD' … WHERE state='AVAILABLE'), or a Redis SET NX PX. The same rules apply.

All-or-nothing across legs

class BookingService {
    Booking bookItinerary(Passenger p, Map<Flight, String> requestedSeats) {
        List<SeatHold> acquired = new ArrayList<>();
        String bookingId = UUID.randomUUID().toString();
        try {
            for (var e : requestedSeats.entrySet()) {
                SeatHold hold = seatMapFor(e.getKey()).hold(e.getValue(), bookingId)
                        .orElseThrow(() -> new SeatUnavailableException(e.getKey(), e.getValue()));
                acquired.add(hold);
            }
            return new Booking(bookingId, p, requestedSeats, BookingStatus.PENDING_PAYMENT, acquired);
        } catch (SeatUnavailableException ex) {
            acquired.forEach(h -> seatMapFor(flightOf(h)).release(h));   // compensate the legs already held
            throw ex;
        }
    }
}

Holding each leg in turn and releasing everything on the first failure is a small, local saga: acquire step by step, and compensate in reverse on failure. Payment then confirms every hold, and if any confirmation fails (a hold expired during a slow payment), the booking is refunded or re-attempted rather than left half-confirmed.

To avoid deadlock-like contention when two customers request the same pair of seats in opposite leg order, acquire holds in a consistent order (by flight ID, for example).

Follow-up questions this topic invites — and their answers

Q: What happens if seat hold succeeds for leg 1 but fails for leg 2 (e.g. the flight fills up between requests)? A: The booking needs to roll back leg 1's hold too — a multi-leg booking should be all-or-nothing, not leaving a passenger with a confirmed seat on one leg of a trip they can't complete, which is exactly why the hold-then-confirm-all-or-release-all pattern matters more here than in the single-leg Movie Ticket case.

Q: How would FareClass pricing interact with dynamic, demand-based airline pricing? A: Similar to Hotel Reservation System's pricing discussion — priceMultiplier as a static field is a simplification; real airline pricing is highly dynamic (demand, days-to-departure, historical booking curves), which argues for a pluggable PricingStrategy rather than a fixed field, following the same Strategy pattern reasoning used throughout this course.

Q: Should seat assignment happen at booking time or be deferrable to check-in? A: Both are legitimate, different product decisions — some airlines assign seats at booking (matching this design directly), others allow booking without a specific seat and assign one at check-in (a different, simpler data model where Booking doesn't need seatAssignments until later) — worth naming as an explicit scope-clarifying question at the start of the exercise, per the Object-Oriented Design Refresher's scoping guidance.

Q: Why store seat state per flight instead of a booked flag on Seat? A: A Seat is part of the aircraft's layout and is reused on every departure. Availability is a fact about one seat on one flight. Putting it on Seat would make row 14C "booked" on every future flight of that aircraft.

Q: How do you test hold expiry without waiting ten minutes? A: Inject a Clock (or a time-supplier interface) into the seat map. Tests use a mutable fake clock, place a hold, advance the clock past the hold duration, and assert that the seat is available again and that the old hold can't be confirmed.

Q: How would overbooking fit into this model? A: Airlines deliberately sell more tickets than seats in a fare bucket, expecting no-shows. That's inventory at the fare-class count level (sell up to capacity × 1.05), separate from seat assignment, which remains strict. Seat maps then allow some bookings to have no seat until check-in.

Advertisement

Previous

Hotel Reservation System

Next

Splitwise / Expense Sharing System

AI Tutor

Lesson: Airline Booking / Seat Selection System

Quick actions

AI responses can be inaccurate. Verify critical information.