A travel search is only as fast as the slowest supplier answering it, unless the system is designed so that it is not. The client's booking engine queried every source in sequence and showed nothing until all had replied, so a single unresponsive supplier held the whole result page hostage. ELIVTECH rebuilt the platform on Laravel with parallel supplier calls, progressive result delivery and a caching strategy that distinguishes between prices that can be reused and prices that must be confirmed.
The client sells flights, hotels and holiday packages sourced from several global distribution systems and direct supplier connections, each with its own contract, latency profile and reliability record. The original engine treated them uniformly and synchronously, which meant search time was dictated by the worst performer and one supplier outage degraded the entire product. Package construction was manual, and a fare shown in results frequently failed on booking because it had been cached past its validity. ELIVTECH rebuilt search as a parallel, progressively rendered operation, introduced a normalised fare model that makes results from different suppliers genuinely comparable, and moved availability and price caching into a layer that respects each supplier's own freshness rules.
At a glance
The challenge
An engine whose speed, accuracy and availability were all set by its least reliable supplier.
Search paced by the slowest supplier
Supplier queries ran in sequence and the result page waited for the final response. A source having a difficult morning added its full latency to every search, and a supplier that timed out could stall the page entirely. Travellers who abandoned mid-search were reacting to an architecture, not to a lack of inventory.
Fares that failed at booking
Results were cached on a single fixed duration regardless of the supplier's own validity rules. Travellers selected a fare, entered passenger details, reached payment and were told the price had changed. Each failure arrived at the most expensive possible moment in the journey, after the traveller had committed effort.
Results that could not be compared
Each supplier expressed baggage allowance, change rules, cancellation terms and taxes in its own shape. The engine displayed them roughly as received, so two apparently identical fares could carry materially different conditions and the traveller had no reliable basis for choosing between them.
Packages assembled by hand
Holiday packages were built manually by the product team and published as fixed combinations. They aged quickly, could not respond to availability, and represented a small share of what the connected inventory could actually have produced if the engine had been able to combine it.
No isolation between suppliers
Supplier integrations shared code paths and error handling. A malformed response from one source raised exceptions that surfaced as a failed search overall, so a partial outage presented to travellers as a total one and support could not tell which connection was responsible.
Post-booking servicing outside the system
Cancellations, date changes and refunds were handled by agents working directly in supplier portals. The platform held no record of what had changed, so its own booking data diverged from reality and reconciliation became a recurring manual exercise.
Our solution
Parallel supplier calls, progressively rendered results, and a fare model that makes comparison honest.
Supplier Modelling and Fare Normalisation
Five weeks mapping each supplier's response shape, latency profile, cache validity rules and failure modes, then designing a normalised fare model covering base price, taxes, baggage, change and cancellation terms. Every supplier response is translated into that model at the integration boundary, so ranking and display work on comparable structures and supplier-specific handling never leaks into the rest of the platform. Fields a supplier does not provide are recorded as unknown rather than defaulted, which keeps the interface honest about what it can and cannot tell the traveller.
Parallel Search and Progressive Delivery
Search dispatches suppliers concurrently with per-supplier timeouts and circuit breakers, so a failing connection is bypassed rather than retried until it stalls the request. Results stream to the traveller as each source replies, with the interface indicating that more are still arriving. A supplier outage now removes its inventory from the page instead of removing the page.
Validity-Aware Caching and Fare Confirmation
Cached results carry the supplier's own validity window rather than a single global duration, and anything approaching expiry is revalidated before display. Selecting a fare triggers a confirmation call to the supplier before the traveller enters passenger details, so a price change is surfaced at the start of the booking rather than at payment.
Dynamic Packaging and Servicing in Platform
Packages are assembled at search time by combining flight and hotel availability against margin and eligibility rules, replacing hand-built combinations. Cancellations, date changes and refunds were brought into the platform as first-class booking transitions that call supplier APIs and record the outcome, so the system's record of a booking matches what the supplier holds. Where a supplier offers no servicing API, the action is queued as a task with the same transition record, so the booking history stays complete even when the work is finished manually.
Technology stack
Results
Measured once all supplier connections were serving through the new search pipeline.
Before and after: platform engineering measures
- Suppliers queried in sequence, page held until the last replied
- One fixed cache duration applied to every supplier
- Price changes surfaced at the payment step
- Fare conditions displayed in each supplier's own shape
- Packages hand-built as fixed combinations
- A single supplier failure presented as a total search failure
- Cancellations and changes performed in supplier portals
- Suppliers queried concurrently with per-supplier timeouts
- Cache validity taken from each supplier's own rules
- Fare confirmed before passenger details are collected
- All fares normalised to one comparable model at the boundary
- Packages assembled at search time from live availability
- Circuit breakers isolate a failing supplier from the rest
- Servicing actions performed and recorded inside the platform
Project timeline
Supplier Analysis and Fare Modelling
Response shape and latency profiling per supplier, cache validity and failure mode documentation, normalised fare model design covering taxes, baggage and change terms, and the target Laravel search architecture.
Search Pipeline and Integration Layer
Concurrent supplier dispatch with per-connection timeouts and circuit breakers, adapter per supplier translating into the normalised model, Redis result caching keyed on validity window, and progressive result streaming to the client.
Booking Flow and Fare Confirmation
Pre-booking fare revalidation, passenger and traveller data capture with consent handling, payment integration with idempotent transaction handling, ticketing and voucher issuance, and booking state modelled as explicit transitions.
Packaging, Search Ranking and Servicing
Dynamic package assembly against margin and eligibility rules, Elasticsearch ranking across normalised fares and hotel attributes, and in-platform cancellation, date change and refund flows calling supplier APIs with recorded outcomes.
Resilience Testing and Cutover
Fault injection per supplier to verify circuit breaker behaviour, load testing on seasonal search profiles, CloudWatch dashboards on per-supplier latency and error rate, then a progressive cutover by travel product.
Key takeaways
What shaped the engineering decisions
- Normalise at the boundary, not at display: Translating supplier responses into one fare model inside each adapter was chosen over handling supplier quirks in the ranking and display layers, which had spread integration knowledge across the codebase and made adding a supplier a change everywhere.
- Stream results, do not wait for completeness: Holding the page until every supplier answered was rejected because it makes the traveller wait for inventory they may not want. Progressive delivery keeps the search usable while slower sources are still arriving.
- Respect each supplier's freshness rules: A single global cache duration was rejected as the direct cause of failed bookings. Caching against the supplier's own validity window, with revalidation near expiry, moved price surprises out of the payment step.
- Isolate suppliers with circuit breakers: Shared error handling turned one bad connection into a total outage. Per-supplier breakers mean a failing source is skipped after a threshold and retried in the background, so its problem stays its own.
- Confirm the fare before collecting details: Revalidating at selection rather than at payment costs one extra supplier call and returns the traveller's time. It also gives a clean point to offer the next best fare instead of ending the journey.
- Bring servicing into the system of record: Agents working in supplier portals left the platform's booking data wrong. Modelling cancellations and changes as booking transitions keeps the record accurate and removes the reconciliation cycle it used to require.
Want results like these?
Let's discuss how ELIVTECH can drive measurable outcomes for your business.
Start your project