Each flight data provider arrives with its own schema, its own authentication, its own rate limits and its own way of failing. Wired in directly, those differences leak into every corner of the application. Then a single provider outage empties the results page for every user, when all it should have removed was the share of the market that provider supplied.
Several providers can sell the same seat and describe it in ways that do not match.
A seat on a flight is created once, by the airline that operates it, and then sold through many channels at the same time: the airline's own systems, the large distribution networks that travel agencies book through, and aggregators and resellers built on top of those. A search product connected to several providers is usually looking at the same underlying seats through several windows at once. Each window carries its own price, its own markup and its own idea of what the price includes.
Each window also describes the seat in its own vocabulary. One physical flight can carry several flight numbers, because partner airlines each sell it under their own code while only one of them actually flies it. Times may arrive in local time or in a universal reference time. One provider names the airport, another names the city, and a city code can cover several airports. A price may or may not include taxes, booking fees or a checked bag, and it may be in a different currency. Durations, layovers and the boundaries between segments are structured differently. None of this is wrong. It is just not the same.
So before two results can be compared, both have to be translated into one shared description: which airline operates each flight, exactly where and when it departs and lands in a common time reference, and what the price covers. Only then can a product recognise that two listings are the same journey, merge them, and keep the better offer. Providers fail in their own vocabularies too. One times out, one returns an error, one slows down under load, and one returns a tidy empty answer that looks exactly like there being no flights. The layer that does the translating is the natural place to notice each of those.
Connecting one provider is a short job. Living with several, each changing and failing on its own schedule, is the larger one. That takes a layer between the providers and the product: one adapter per provider, a single internal shape behind all of them, deduplication across sources that sell the same seats, and every failure held inside the adapter where it started so it never reaches the search core.
Multi-provider search usually breaks at the seams between providers.
When providers are integrated inline, each one's quirks end up spread through the application. The ranking code grows a special case for one provider's time format. The pricing code remembers that another leaves out a fee. Changing or adding a provider means touching all of it, and every change risks breaking something that had nothing to do with that provider.
The more damaging pattern is failure that travels. A search that waits for every provider before answering is only as available as its least reliable source. If one provider is down, rate limiting or slow, the whole search fails or times out and the user sees an empty page. Most of the market was available the whole time, and none of it was shown.
The reflex, and the fix.
Integrate each provider where it is called
It is the shortest path to a working call, and there is no abstraction to design.
Every provider change then ripples through the application, and one provider outage returns an empty result set when it should return a smaller one.
An adapter per provider behind one interface
An internal schema to design and maintain, and a translation layer between it and every provider.
Provider changes stop at the adapter. Overlapping inventory is deduplicated once. A failing provider makes the result set smaller and leaves it standing.
Put the boundary at the provider, because that is where the variation lives and where failures start.
One adapter per provider
Each adapter owns its provider's schema, authentication and rate-limit behaviour, and hands back results in the canonical internal shape. Nothing above the interface knows, or needs to know, which provider answered.
A canonical internal result
The search core reasons about one shape. Ranking, viability and pricing logic are each written once, against that shape, and never again per provider.
Deduplication across overlapping sources
Providers resell overlapping inventory, so the same itinerary arrives more than once. Merging happens after normalisation, because that is the first point at which two results from different providers can be compared at all.
A circuit breaker on every adapter
A provider that starts failing is cut off at its own adapter, with a fallback, so the search returns the rest of the market instead of nothing.
What each choice cost, and what it bought.
ADrawing the boundary at the provider
Every provider gets its own adapter, and that adapter is the only code that knows the provider exists. The cost is an adapter to write and maintain for each source, and a translation step on every response. What it buys is that a provider changing its format, its authentication or its limits becomes a change in one file. Adding a provider means writing an adapter, and the search core is left alone. Inline integration spreads each provider's variation across the codebase, so the cost of every change grows with the number of providers.
BDesigning one canonical result
A single internal shape had to be designed up front, which meant deciding how to represent everything providers disagree about: codeshare flight numbers, time references, what a price includes. That shape then becomes something the team owns and maintains. In return, ranking, viability and pricing are written once. Without it, every rule in the product would be multiplied by the number of providers, and each copy would drift from the others.
CDeduplicating after normalisation
Two listings of the same journey cannot be matched while they are still in their providers' formats, because they do not even name the flight the same way. So merging happens after translation, on the shared description. The merge step has to decide when two results are the same journey and which offer survives, and that logic needs care. It is what took duplicate results from 12.2 to 1.2 percent, and it is why a traveller no longer sees the same flight twice at two prices.
DContaining failure with a circuit breaker per adapter
A circuit breaker watches a provider's responses. When failures pile up, it stops sending that provider traffic for a while and answers from a fallback, then tries again later to see whether the provider has recovered. The cost is deliberate: during that pause the product does not ask a provider that might have answered, and loses whatever it would have returned. What it buys is that one failing source costs its own share of the results and nothing else. Empty results caused by provider failure fell from 6.8 to 0.9 percent. Retrying a struggling provider from every search is the alternative, and it tends to keep the provider down and hold every search open.
A single headline number hides where a system fails. This work was scored on the dimensions that actually decide whether it holds in production, measured on real, held-out cases rather than the demo path.
Success, failure, duplicate and empty-result figures are measured before and after, on the delivered system.
Provider calls scattered through the application, the same itinerary listed more than once, and an empty results page whenever any one provider goes down.
An isolation layer that took API success from 90.6 to 98.95 percent, search failures from 8.4 to 1.2 percent, duplicate results from 12.2 to 1.2 percent, and empty results caused by provider failure from 6.8 to 0.9 percent.
What it owns, and what it hands to a person.
The layer contains provider failure. It cannot produce the inventory a failed provider would have returned, so a degraded search gives an honestly smaller result set and never one dressed up as complete. It also cannot tell when a provider returns well-formed data that is simply wrong. That case is flagged, and it stays outside what the layer can guarantee.
Any integration with several sources: one adapter per source, one internal shape, deduplication after normalising, and failure contained in the adapter so it never reaches the core.
The same seat is usually sold through several providers at once, so a multi-provider search will see duplicates by design and has to merge them deliberately.
Two listings can only be compared once both are translated into one shared description, including who operates the flight, a common time reference and what the price includes.
Put the integration boundary where the variation is. One adapter per source keeps each source's quirks and failures out of the core.
A search that waits for every source is only as available as the worst one. Contain failure per source so an outage shrinks the results and leaves the page standing.
A failing source can look like an empty one. Treat a sudden silence from a provider as a possible fault until you know otherwise.