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).
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.
- 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®ion=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).
- `lift_ctr`, `lift_conversion`, «explore vs exploit» доля.
- % 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.