The Problem

Operators in East‑Africa and West‑Africa need a betting back‑office that can handle high‑frequency sports wagers, instant‑settlement crash games, and local mobile‑money payments while staying compliant with the BCLB tax and KYC rules. Existing platforms either lack real‑time odds, do not integrate M‑Pesa/Flutterwave, or require heavyweight monoliths that are hard to scale.

What This Does

The repo ships a Tier‑1 betting platform built in Go with a microservice‑style layout. Core services live under cmd/:

  • cmd/gateway/main.go – public API gateway (REST + WebSocket) handling auth, rate‑limiting, and routing.
  • cmd/engine/main.go – betting‑logic engine that calculates odds and validates bets.
  • cmd/games/main.go – crash‑game engine with provably‑fair calculations.
  • cmd/wallet/main.go – atomic wallet service that debits/credits balances in a single DB transaction.
  • cmd/settlement/main.go – payout processor that settles winning bets.

Shared domain models (internal/core/domain/*.go) and business use‑cases (internal/core/usecase/*.go) are reused across services, while infrastructure concerns (DB, NATS, Redis, logging, validation) live in internal/infrastructure/. Database migrations are in migrations/. Docker and Compose files enable containerised development and deployment.

How It Is Wired

Execution starts at the gateway (main in cmd/gateway/main.go). The main function loads configuration via LoadConfig (calls getEnv* 48 + 26 + 19 + 18 + 13 times) and registers HTTP routes (RegisterRoutes invoked 14 ×). Each route handler lives in internal/transport/http/*.go; for example, NewLiveHandler in live_types.go creates a handler that:

  1. Extracts the user ID (getUserID).
  2. Calls WriteError or WriteJSON (used 44 × and 45 × across the codebase).

When a bet is placed, the call chain is: main → RegisterRoutes → NewLiveHandler → Execute (internal/core/usecase/place_bet.go)ApplyTx (DB transaction via ExecContext). The transaction writes to PostgreSQL (internal/infrastructure/database/connection.go), and the wallet service updates balances atomically (CreateBetWithWalletUpdate).

The engine service (cmd/engine/main.go) follows a similar pattern but focuses on odds calculation (syncOddsFromProvider) and publishes events via the NATS wrapper (internal/infrastructure/events/bus.goPublish called from 26 files).

The crash service (cmd/games/main.go) runs a WebSocket loop (Run in crash_engine.go) that repeatedly calls handlePlaceBetCreateBetWithWalletUpdate → DB write, then streams results to connected clients.

Key hubs in the call graph are functions with the widest blast radius: Close (61 callers), New (52 callers), generateID (47 callers), and WriteJSON (45 callers). No circular imports were detected, and the internal import graph consists of three modules with zero edges, indicating low coupling between the self‑contained projects.

How To Use It

# Clone the repo
git clone https://github.com/moses-y/lets-bet.git
cd lets-bet

# Build all services (Makefile target)
make build

# Load database schema
go run cmd/migrate/main.go

# Start locally with Docker Compose (uses docker-compose.yml)
docker compose up -d

Configuration values are read from environment variables; a template is provided in .env.example. Services expect PostgreSQL connection strings, Redis host, NATS URL, and payment provider credentials (M‑Pesa, Flutterwave) as documented in docs/PROJECT_OVERVIEW.md.

To run a single service, invoke its binary, e.g.:

./bin/gateway   # starts the API gateway on port 8080

Real‑World Use

A Kenyan operator would deploy the Docker stack behind Cloudflare, configure M‑Pesa Daraja credentials, and point the gateway to a domain. A user places a sports bet via the REST endpoint; the gateway validates the request, the wallet service reserves funds, the engine computes odds, and the settlement service pays out winnings automatically, all while publishing audit events to NATS for downstream reporting.

Code Health & Issues

  • High – Pin third‑party GitHub Actions to a commit SHA (.github/workflows/*.yml).
  • Medium – Enable Dependabot (.github/dependabot.yml missing).
  • Medium – Pin Docker base images by digest (deployments/docker/Dockerfile).
  • Medium – Set persist-credentials: false on checkout step (.github/workflows/ci.yml).
  • Low – Add timeout-minutes to workflow jobs (.github/workflows/ci.yml).

Static analysis also flagged duplicated 6‑line blocks across 86 files (e.g., in cmd/engine/main.go, cmd/settlement/main.go, cmd/gateway/main.go), deep nesting (max depth 6) in several middleware files, and high branching density in tax and compliance helpers. No critical or low‑severity bugs were detected.

The Bottom Line

The repository delivers a production‑grade, Go‑based betting stack with clear separation of concerns and a solid test suite, but the codebase suffers from noticeable duplication and some CI/CD hygiene gaps. Teams comfortable with Go, Docker, and NATS can adopt it quickly; they should address the high‑impact duplication and tighten CI security before running in a regulated environment.