A European fan engagement platform connects sports clubs, advertisers, agents, and fans through a digital reward economy.
As the product expanded, the challenge stopped being simply to build more features. The first version had to become a system capable of carrying real users, financial value, AI-assisted decisions, operational load, and continued product change without losing control.
Fans complete advertising tasks and earn on-chain tokens. Clubs receive matched stablecoin allocations from advertising budgets. Agents drive growth through referrals.
Real money moves through the platform. AI evaluates user submissions before rewards are released. Referral, task, treasury, and blockchain workflows cross multiple services. Every critical operation has to remain correct through retries, races, abuse, delayed events, and partial infrastructure failure.
The platform had outgrown the assumptions of its first version. Product domains overlapped, financial responsibilities needed stronger ownership, external AI had become operationally critical, multi-chain behavior required reusable boundaries, and realistic load and failure behavior needed to become visible.
The concept is easy to explain. The production system behind it is not.
Real money moves through the platform. AI evaluates user submissions before rewards are released. Referral, task, treasury, and blockchain workflows cross multiple services. Every critical operation has to remain correct through retries, races, abuse, delayed events, and partial infrastructure failure.
As the product expanded, the challenge stopped being simply to build more features. The system needed a different level of production maturity:
This was not a blockchain task, an AI task, or a backend task in isolation. It was a product-engineering problem spanning all of them.
We built and evolved around thirty deployable services and applications in Go, organized around bounded contexts including identity, clubs, referrals, the advertising-task exchange, treasury, leaderboards, NFT, and notifications. Services communicate over gRPC; cross-domain state propagates through Kafka using transactional outbox patterns and dead-letter queues. Canonical state remains in Postgres; Kafka acts as a notification and replay channel, never as the source of truth. The architecture was designed not only to distribute load, but to make ownership, failure modes, and future product change more explicit.
Reward logic was extracted into a dedicated economy service: an idempotent transaction journal, integer-only amounts throughout the reward path, a two-phase reserve, commit, and release pattern for converting rewards into on-chain tokens, and database-enforced constraints allowing exactly one active reserve per account. Product services decide what should be rewarded. Only the treasury can move balances. This separation made financial correctness a property of the system rather than a convention shared across feature teams.
The original identity service had gradually absorbed clubs, referrals, content, and support concerns. We decomposed it into bounded services using a strangler approach. Public APIs remained stable behind the BFF and gateway layer while ownership moved out service by service. The platform continued operating while the internal structure became easier to reason about, change, and scale.
We built a reusable integration layer covering more than twenty networks across EVM, BTC-like, Tron-like, and Kaspa families. The layer provides unified interfaces for transaction lookup, balances, address validation, and token operations, with chain-specific adapters isolated behind an anti-corruption boundary. The result is production infrastructure that lets the product work across different networks without allowing chain-specific behavior to leak through the rest of the platform.
User-submitted content is evaluated by an external AI analyzer before rewards are paid. We treated the analyzer as an unreliable external dependency inside a critical product path and built a stable boundary around it: moderation verdicts cached by canonical URL and instruction hash, per-channel reasoning preserved when submissions are rejected, admission control that reserves analyzer capacity for user-facing decisions, and background work prevented from starving the moderation queue. AI became an operable product component with explicit capacity and failure behavior, not a best-effort API call attached to the reward flow.
The reward economy required layered protection against automated and coordinated abuse. We implemented captcha, OTP and trusted-device checks, Redis/Lua rate limiting, device and IP fingerprinting, and asynchronous AI moderation. The controls were designed as a system: strong enough to protect money and incentives, but selective enough not to block normal users unnecessarily.
We created reproducible k6 scenarios for registration, club onboarding, referrals, leaderboards, and task flows. The platform is load-tested to 900 complete registration flows per second. A single flow contains fourteen HTTP calls, equivalent to approximately 12,600 requests per second while writing real rows and publishing real events. The purpose was not to produce an isolated benchmark. It was to test the product through complete workflows that exercise application state, persistence, and asynchronous infrastructure together.
The platform runs on Kubernetes with a reusable Helm chart and per-service values across environments. The delivery and operations layer includes Vault-based secrets, GitHub Actions CI/CD, Prometheus and Grafana observability, and Kafka consumer-lag alerting. Production maturity meant making deployment, configuration, monitoring, and failure visible parts of the system rather than work performed manually around it.
A dedicated concurrency test fires eight simultaneous reserve attempts against one account and asserts that exactly one succeeds, the balance never becomes negative, and replaying a commit returns the same transaction without duplicating journal rows. Correctness is enforced and tested at the point where concurrency can cause financial loss.
Payout events carry only {task_progress_id, version}. Consumers hydrate the required state from the database. Kafka remains replayable without becoming a secondary store of personal data.
Reward points are integer-only. On-chain amounts are stored as wei-scale numerics and exposed as decimal strings. There is no floating-point arithmetic anywhere in the money path. Precision loss is prevented by design rather than avoided through developer discipline.
Growth exposes assumptions hidden in the first version: domains overlap, financial logic spreads across services, external AI becomes operationally critical, infrastructure starts resisting the roadmap, and failures become more expensive.
The solution is not simply more code or a larger team. The product needs clearer boundaries, stronger guarantees, explicit failure behavior, and an architecture that reflects what the business has become.
This platform required product decisions, distributed systems, financial correctness, production AI, multi-chain infrastructure, anti-abuse controls, and operations to work as one coherent system.
That transition — from an ambitious product to a system capable of carrying its ambition — is where Halfsteel works.
Tell us what the next stage requires and what is starting to break.
Discuss your next stage