Corporate project · Presented company-agnostic: architecture and decisions only.
Sports Wagering Platform
Event-driven bet intake, validation and settlement pipeline on Kafka and .NET
Status
In production
Role
Senior backend engineer: service and pipeline design, Kafka handlers, live-limits engine, live-bet validation and hand-off, resilience, CI/CD and Kubernetes packaging.
Architecture
Kafka-decoupled intake and validation
Shared limits engine
Per-player locks, idempotent hand-off to the live API
The wager is accepted and queued. Limits and the player's history are checked, then the accepted wager is handed to the live engine's bet API, which inserts it into the live DB.
Architecture · 7 nodes · 2 flows
1Betting front-end → Bet intake APISubmit wager (HTTPS)
Service / compute
Data store
Queue / messaging
Client
Synchronous
Async / loop
Scroll sideways to see the full diagram
How it flows, step by step
Click a step to jump to it. Click a component for details.
What it does
Backend microservice suite for an online sports betting operator. It accepts pregame and live wagers, validates them against player limits and live odds, persists them into the bet ledger and pushes every state change back to the front-end in real time. Live wagers add history-aware limits checked under per-player locks, then an idempotent hand-off to the live engine's bet API.
The problem
A monolithic, synchronous bet-insertion path could not absorb peak event traffic, gave players no feedback while a bet was being validated, and tied limit rules to a legacy stored-procedure model that was hard to test or evolve. The operator needed to decouple intake from validation and persistence, keep strict ordering per player, and introduce richer live-betting limit rules without breaking the legacy ledger.
What I built
Designed a Kafka-decoupled pipeline (intake, validation, insertion) with a dedicated notification stream, so every wager moves through explicit states and the player always sees progress.
Extracted a deterministic, infrastructure-free limits engine as a versioned NuGet package (static limits plus history-aware per-game caps and surpass rules) shared by the API, the validation service and a new Blazor client.
Made live-bet validation concurrency-safe using per-player application locks and bounded semaphores for catalog lookups, rejecting cleanly when upstream limit data is missing or inconsistent.
Added idempotency and age thresholds (stale wagers are rejected at both validation and persistence), plus stable transaction identifiers propagated through structured logs and notifications.
Wrapped the legacy bet-insertion library behind an HTTP legacy ledger API so the new pipeline could ship incrementally while the legacy ledger remained the system of record.
Shipped multi-environment CI/CD: unit and end-to-end test stages, container builds, Helm-based rollouts, and parallel IIS deployment for legacy-hosted components.
Key decisions and why
01
Kafka as the backbone between intake, validation and persistence
Decoupling lets the API acknowledge instantly, absorbs event-day spikes, and allows each stage to scale and fail independently. Messages are keyed by player so ordering is preserved per account, with manual offset commits and bounded retries.
02
Limits engine as a pure library, not a service
Keeping the rules free of HTTP, database and Kafka dependencies makes them fully unit-testable and reusable on the server and in the browser. Callers supply a history snapshot; the engine only decides. Versions are pinned explicitly so contract changes are revalidated together.
03
Authoritative second validation under a per-player lock
A live wager is re-validated against the freshest limit snapshot and again under a per-player lock, so concurrent bets cannot jointly breach per-game caps. The public pre-check endpoint is explicitly preliminary.
04
Dedicated notification stream feeding SignalR
Every service emits wager state changes to one topic; a single hub fans them out to clients. Services never talk to the front-end directly, which keeps them stateless and horizontally scalable.
05
Strangler-style wrapper around the legacy insertion path
Re-implementing years of ledger rules was riskier than wrapping them. The legacy library is hosted behind an HTTP API, giving the new pipeline a stable contract while the rules are migrated feature by feature.
06
Resilient HTTP clients with explicit timeouts
All upstream calls (odds, player profile, legacy API) use typed clients with retry, timeout and circuit-breaker policies. Failures translate into explicit business error keys instead of silent drops.