What Is a Travel Itinerary Service?
Users plan and manage multi-component trips: flights, hotels, car rentals, and activities stitched together in a timeline. The low-level design must handle heterogeneous booking types, time-zone-aware scheduling, conflict detection, and collaborative sharing between travelers.
Data Model
-- Core tables (SQL pseudocode)
CREATE TABLE itineraries (
itinerary_id BIGINT PRIMARY KEY,
owner_user_id BIGINT,
title VARCHAR(255),
start_date DATE,
end_date DATE,
status VARCHAR(32), -- 'DRAFT', 'CONFIRMED', 'COMPLETED', 'CANCELLED'
currency CHAR(3),
created_at TIMESTAMP,
updated_at TIMESTAMP
);
CREATE TABLE itinerary_items (
item_id BIGINT PRIMARY KEY,
itinerary_id BIGINT REFERENCES itineraries(itinerary_id),
item_type VARCHAR(32), -- 'FLIGHT', 'HOTEL', 'CAR', 'ACTIVITY'
external_ref_id VARCHAR(128), -- booking ID from external service
title VARCHAR(255),
start_datetime TIMESTAMP WITH TIME ZONE,
end_datetime TIMESTAMP WITH TIME ZONE,
location VARCHAR(255),
cost DECIMAL(10,2),
currency CHAR(3),
status VARCHAR(32), -- 'PENDING', 'CONFIRMED', 'CANCELLED'
notes TEXT,
sort_order INT
);
CREATE TABLE itinerary_collaborators (
itinerary_id BIGINT REFERENCES itineraries(itinerary_id),
user_id BIGINT,
role VARCHAR(16), -- 'VIEWER', 'EDITOR', 'OWNER'
invited_at TIMESTAMP,
accepted_at TIMESTAMP,
PRIMARY KEY (itinerary_id, user_id)
);
CREATE TABLE itinerary_documents (
doc_id BIGINT PRIMARY KEY,
itinerary_id BIGINT REFERENCES itineraries(itinerary_id),
doc_type VARCHAR(32), -- 'TICKET', 'VOUCHER', 'PASSPORT'
storage_url VARCHAR(512),
uploaded_at TIMESTAMP
);
Core Algorithm: Conflict Detection
When a user adds or moves an itinerary item, the service checks for time overlaps against existing confirmed items. The overlap condition for two intervals A and B is: A.start < B.end AND A.end > B.start. All datetime comparisons are performed in UTC after normalizing from local time zones using the item location’s IANA timezone.
- On item insert or update, load all confirmed items for the itinerary.
- Convert all timestamps to UTC.
- Check the new item against each existing item for overlap.
- Return a list of conflicts (not a hard block, since layovers and quick transfers are valid in some cases); flag them as warnings in the UI.
Aggregation Workflow: Building the Timeline
The itinerary timeline is assembled by:
- Fetching all
itinerary_itemsfor the givenitinerary_id, ordered bystart_datetime ASC, sort_order ASC. - Grouping items by local calendar date at the destination timezone to produce a day-by-day view.
- Computing gaps between consecutive items and surfacing them as free time slots in the UI.
- Aggregating total cost across all items grouped by currency, with an optional FX conversion to the itinerary base currency using a daily exchange rate lookup.
Failure Handling
- For partial booking failures, if a flight books but the hotel call fails, the itinerary item for the hotel is left in
PENDINGstatus and a retry job is enqueued. The user is notified of the partial failure and can manually retry or substitute. - For cancellation propagation, cancelling a flight that is the only way to reach a hotel destination triggers a warning that downstream hotel and activity items may need to be cancelled too. The system does not auto-cancel dependents; it surfaces a checklist.
- For collaborative edit conflicts, two editors moving the same item simultaneously use optimistic locking via an
updated_atversion check. The second writer receives a conflict error and must reload before saving. - For time-zone edge cases, all stored timestamps are UTC. Display conversion happens client-side using the item location timezone, preventing DST ambiguity in stored data.
Scalability Considerations
- The read-heavy profile matters: itinerary reads (viewing the timeline) vastly outnumber writes. Cache the assembled timeline in Redis, keyed by
itinerary_id:version, and invalidate on any item write. - For document storage, tickets and vouchers are stored in object storage (S3-compatible) with signed URLs; the database holds only the metadata reference.
- For event-driven updates, external booking status changes (flight delay, hotel confirmation) arrive as webhook events and are processed by a consumer that updates the relevant
itinerary_itemsrow and pushes a real-time notification via WebSocket or push notification. - For sharing at scale, public share links generate a read-only token stored alongside the itinerary. Shared views are served from a CDN-cached snapshot refreshed on write, eliminating database load for viral itinerary shares.
- For search, users search their past and upcoming itineraries by destination or date range. Index
itinerariesin Elasticsearch with denormalized location and date fields to support fast full-text and range queries without scanning the main database.
See also: Airbnb Interview Guide 2026: Search Systems, Trust and Safety, and Full-Stack Engineering
See also: Scale AI Interview Guide 2026: Data Infrastructure, RLHF Pipelines, and ML Engineering
Keep sharpening your system design:
