The challenge
A betting platform does not generate its own reality. Fixtures, competitions, participants, odds, live scores and results all arrive from external providers, and the platform is only as correct as what it can make of them.
The difficulty is that no two providers describe the same sport the same way. They use different identifiers for the same fixture, different names for the same team, different taxonomies for market types, different rules for when a match is considered started, and different conventions for how a handicap line is expressed. They deliver by different mechanisms, on different schedules, with different reliability.
Then, having modelled the world differently, they disagree about it. Two providers can report different scores for the same match at the same moment, and one of them is wrong.
Approach
One canonical model, providers behind adapters. The platform defines its own model of competitions, fixtures, markets and outcomes. Each provider gets an adapter whose only job is to translate into that model. Nothing downstream knows which provider anything came from.
This is the decision everything else depends on. Without it, provider-specific handling leaks into trading, settlement and the front end, and adding a provider means touching all of them.
Entity resolution is a first-class problem. The same fixture from two providers arrives with two unrelated identifiers, two spellings of each team, and sometimes two kick-off times a few minutes apart. Matching them is what allows one provider to cover for another, and it cannot be done reliably by name comparison alone.
The approach that works is a persistent mapping table between provider identifiers and canonical entities, seeded by automated matching on a combination of competition, participants and start time, with anything ambiguous surfaced for a human decision rather than guessed. Once a mapping is confirmed it is permanent, so the problem shrinks over time instead of recurring on every fixture.
Getting this wrong is expensive in a specific way: two canonical fixtures for one real match means split liability and markets that cannot be settled coherently.
Push and pull are normalised early. Some providers stream over a persistent connection. Others expose an endpoint to poll. These have completely different failure characteristics, and allowing that difference to propagate means every consumer needs to understand both.
Adapters convert either into the same internal event stream, so the rest of the platform sees one kind of update regardless of how it arrived.
Recovery assumes disconnection, because disconnection is routine. A dropped streaming connection means missed messages, and reconnecting to the live stream leaves a silent gap.
The pattern that holds is snapshot plus delta: on reconnect, request a full state snapshot, apply it, then resume the delta stream from the point the snapshot represents, discarding deltas already reflected in it. This requires sequence numbers to be tracked per stream and the snapshot to be reconcilable against them. Providers vary in how well they support this, and the adapter is where the difference is absorbed.
Ordering and duplication are handled by sequence, not arrival. Messages arrive out of order and more than once, particularly around reconnection. Every update carries a provider sequence or revision, and an update older than the current state is discarded by a conditional write rather than by application logic that races under load.
Provider precedence is explicit. When sources conflict, the platform needs a rule, not a race. Precedence is configured per data type and per competition, because providers are not uniformly good: one may have the best live coverage for a top-tier league while another is more reliable for lower divisions or for settlement results.
Failover follows the same configuration. If the preferred provider goes quiet, the next takes over, and the entity mapping is what makes that switch coherent rather than a duplication event.
Staleness is monitored, not assumed. The dangerous failure is not a provider going down, which is obvious. It is a provider staying connected and quietly stopping sending, where everything appears healthy while odds slowly become wrong.
Feed health is measured by expected message rate per source and time since last update per active fixture, with alerting on silence rather than on errors. During a live match, several seconds without an update is itself information.
Volume is spiky and concentrated. A Saturday afternoon with many simultaneous fixtures produces message rates far above the daily average, arriving in bursts. The pipeline is built to buffer and apply in order rather than to process synchronously, so a burst delays updates slightly instead of dropping them.
Result corrections are expected, not exceptional. Providers revise results after review. Because the platform stores results by revision and settles idempotently, a correction is applied through the same path as the original, and downstream settlement reverses and re-settles rather than double-paying.
Results
The platform consumes several data providers behind one canonical model, normalising streaming and polling sources into a single internal pipeline, with snapshot and delta reconciliation after any disconnect and configurable provider precedence with automatic failover.
The part that has proven most valuable is the least visible. Because entity mapping is persistent and provider handling is contained in adapters, adding a provider is an adapter and a mapping exercise rather than a change to the platform, and losing one during a live event is a configuration failover rather than an incident.