GH GambleHub

Content catalog engine

The catalog engine is the core of game showcases and promo collections at the front: it collects and normalizes metadata from providers (RGS), provides search/filters/ranking, applies accessibility rules by jurisdiction and brand, mixes personalization and promo placements, and then delivers quick responses via API with predictable SLO.


1) Goals and principles

Fast reads: p95 ≤ 100-150 ms per directory/search request.
Truth and freshness: guaranteed relevance of key attributes (availability, jackpots, provider statuses).
Flexibility: editorial collections and promo slots without releases.
Compliance: geo/age/content rules, licenses, responsible play restrictions.
Multi-tenant/region: brand isolation and data residency compliance.
Observability: Outing Quality Metrics, A/B, Game Conversion/Bet.


2) Domain model (minimum)

Entities:
  • Game is a game/product of the provider.
  • Provider - RGS/studio.
  • Variant - variants of one game (volatility, lines, limits, server).
  • Collection - editorial/automatic selection (e.g. "New Items," "Jackpots").
  • Placement - fixed position/banner/tile on the page/in the slot.
  • Capability/Feature - game attributes (free spins, buy feature, jackpot).
  • JurisdictionRule - accessibility/restriction rules.
  • Signals - behavioral/operational signals (popularity, CTR, revenue).
  • Asset - media (icons, posters, demo videos) with options for devices/densities.

Keys: 'game _ id' (stable internal, not equal to provider_game_id), 'tenant _ id', 'region', 'locale'.


3) Ingest and normalization

Conveyor:

1. Source Adapters (pullers): integrations with RGS/studios (directories, features, RTP, tags).

2. Sanitize & Map - Map external fields into a single dictionary (ACL), validate, and deduplicate.

3. Enrich: localizations, categories, semantic tags, age limit ratings.

4. Moderate: content flags (NSFW/religious symbols/sensitive topics) by market.

5. Publish: 'GameUpserted/ProviderStatusChanged' events → catalog projection.

Idempotence: all messages with 'source _ id' + 'version _ ts'; repeat is processed without side effects.
Evolution scheme: 'schema _ version' in adapters + mapper migrations.


4) Normalized scheme (simplified)

json
{
"game_id": "g_3f92",
"tenant_id": "brand_eu",
"provider": { "id": "pr_evolution", "name": "Evolution" },
"title": { "en": "Lightning Roulette", "de": "Lightning Roulette" },
"capabilities": ["live","roulette","multiplier","bonus"],
"rtp": 97.3,
"volatility": "high",
"limits": { "min": 0.1, "max": 1000.0, "currency": "EUR" },
"jurisdiction": {
"allowed": ["MT","EE","DE"],
"blocked": ["NL","BE"],
"age_rating": 21
},
"assets": {
"tile": { "1x":"...", "2x":"..." },
"poster": { "web":"...", "mobile":"..." }
},
"tags": ["new","jackpot"],
"release_date": "2025-09-12",
"status": "active",
"variants": [{ "id":"v1","server":"eu-central-1","rtp":97.3 }]
}

5) Search, filters, facets

Indexes: full text by name/synonyms, facets by 'provider', 'capabilities', 'volatility', 'rtp _ bucket', 'tags'.
Filters: jurisdiction/region/language/device/age, active/certified only.
Synonyms/stemming: a map of user terms ("books," "fruits," "balls").
Typos: tolerant search (edit distance ≤1 -2) with length restriction.


6) Ranking: signals and formula

Signals (example):
  • Freshness (time since release).
  • Population (starts/hour, unique players).
  • Quality (CTR from catalog to game, hold 1/7 day).
  • Business (marketing boosts, deals, promotional slots).
  • Compliance (soft downgrades for sensitive content, if required).
  • Player-fit (profile/preference compatibility).
Combination (concept):

score = w1freshness + w2popularity + w3ctr + w4player_fit + w5boost

Weights are controlled by configuration/experiments; all signals normalized to [0; 1].


7) Personalization

Short memory: recent launches and genres, RYW - the user immediately sees fresh action.
Long memory: Game and player profile embeddings (game genres/volatility/sessions).
Security: Personalization never violates jurisdictional/age rules.
Fallback: if there are few signals - neutral ranking + editorial collections.


8) Collections and promotional placements

Collections:
  • Auto: rule/query (e.g. 'capabilities contains' jackpot 'AND release_date> = NOW () -30d').
  • Editorial: manual list with order and timing.
  • Placements: fixed positions on pages (hero, row-1-slot-3), A/B, targeting by segment/jurisdiction.
  • Dates and priorities: 'starts _ at/ends _ at', collision priority, preview before publication.

9) Compliance and Accessibility Policy

Geo/jurisdiction: white/black lists of countries/regions, verification of licenses/certificates.
Age rating: minimum age, warnings, hiding for incompatible markets.
Subject/symbolism: flags of sensitive content by country (religion, alcohol, etc.).
Responsible play: Hide/demote for limit/timeout players.
Audit: immutable log of changes in availability with reasons.


10) Multi-tenant and multi-region

All data is marked 'tenant _ id' and 'region'.
Isolation: quorums/vaults by region; cross-regional projections - aggregates only.
Fairness: quotas for ingest/publications per tenant so that the "noisy" brand does not delay the rest.


11) Architectural outline

Write-core directory (CP): normalization + transactional outbox events.
Projections/Read Models (EC): search indices, materialized collections, popularity counters.

Cache layers:
  • Edge/CDN for cold pages/images.
  • In-memory caches for hot queries (key = filters + page + tenant + region).
  • Ficheflags: rolling ranking/collection rules without release.

12) API (REST/GraphQL, examples)

REST


GET /v1/catalog?tenant=brand_eu&region=EE&locale=ru
&filter=jackpot,true&sort=score_desc&page=1&size=24
→ 200 { items:[...], facets:{...}, as_of:"2025-10-31T12:10:02Z" }

GraphQL (fragment)

graphql query Catalog($tenant:String!,$region:String!,$q:String,$filters:Filters){
catalog(tenant:$tenant, region:$region, q:$q, filters:$filters){
items { gameId title provider { name } score badges assets { tile } }
facets { providers { key,count } capabilities { key,count } }
freshnessMs
}
}
Contracts:
  • Always return 'as _ of/freshnessMs', paging, facets.
  • For personalization - session marker (RYW) without PII.

13) Signals and data flow

Popularity: increments when launching games → minute buckets → units in projection.
CTR/conversion: Click/launch counters on placements/collections.
Operating statuses: health providers (RGS), jackpots/limits (event stream).
Marketing boosts: time factors for games/categories/suppliers.


14) Observability and SLO

Directory metrics:
  • `catalog_p95_ms`, `catalog_p99_ms`, `error_rate`.
  • 'index _ freshness _ ms' (project delay), 'ingest _ lag _ ms'.
  • 'ctr ',' click-to-launch ',' collection _ coverage '(% check out from collections).
Personalization:
  • `lift_ctr`, `lift_conversion`, «explore vs exploit» доля.
Compliance:
  • % correctly applied geo/age rules, number of blocks/hour.

Alerts: growth of'ingest _ lag _ ms', drop in CTR on key collections, degradation of the provider (tags in the issue).


15) Performance and caching

Strategy: hot queries - cache for 30-120 s with a key by filters; personal blocks - short TTL (10-30 s) or no cache.
Disability: By 'GameUpserted/AvailabilityChanged/PlacementUpdated' events.
Pagination: stable cursors so as not to "jump" cards when updating signals.


16) Working with media

Render profiles: sizes/densities for web/mobile/TV.
Optimization: WebP/AVIF, lazy-load, sprite/atlas for tiles.
Content security: scanning, watermarks, inline-PII prohibition.


17) Testing

Contract/Schema tests for adapters and APIs.
Relevancy tests: golden sets of queries → expected results/order.
Personalization: offline AUC/NDCG + online A/B with guardrail metrics (in-game time, deposits, RG signals).
Chaos: provider degradation, ingest spikes, indexing delays.


18) Playbooks (runbooks)

1. Index lag> SLO: stop secondary collections, increase the priority of ingest, temporarily simplify ranking.
2. Provider "red": lower/hide his games, raise alternative collections.
3. API error jump: check cache/backend, enable security timeouts, reduce page size.
4. Incorrect availability: roll back the last rule, include a "white list" of critical markets, audit changes.
5. Ranking release: canary rollout (5% → 25% → 50% → 100%), CTR/conversion rollback.


19) Typical errors

Mixing external provider schemas with an internal model (no ACL).
The absence of'as _ of/freshness' → disputes about the "outdated" directory.
Personalization that violates jurisdictional rules.
The only "magic" ranking formula without decomposition of signals and A/B.

Large pages without caching and cursors → p99 "shoot."

Dual-write to index and OLTP instead of events + projections.


20) Pre-sale checklist

  • Normalized field dictionary and ACL for all providers.
  • Idempotent ingest, outbox/inbox, DLQ and redrive.
  • Catalog projections and search indices with freshness SLO.
  • Weight-controlled ranking, signal decomposition and A/B.
  • Compliance rules (geo/age/topic) and audit of changes.
  • Multi-tenant/region: data isolation, fairness, residency.
  • API with'as _ of ', facets, cursors; cache and disability by event.
  • p95/p99 metrics, ingest/indexing, CTR/conversion; alerts.
  • Incident playbooks; canary releases and ficheflags.
  • Tests of relevance, contracts, chaos and personalization.

Conclusion

The catalog engine is a "search engine + rule system + showcase" over game content. Strong ACL, normalized data, projections for quick reads, transparent ranking signals, personalization with guardrail metrics and strict compliance turn the catalog into a sustainable and measurable product lever of growth - without surprises in production and without compromises with regulators.

Contact

Get in Touch

Reach out with any questions or support needs.We are always ready to help!

Start Integration

Email is required. Telegram or WhatsApp — optional.

Your Name optional
Email optional
Subject optional
Message optional
Telegram optional
@
If you include Telegram — we will reply there as well, in addition to Email.
WhatsApp optional
Format: +country code and number (e.g., +380XXXXXXXXX).

By clicking this button, you agree to data processing.