Files
comunidadhll/docs/historical-stats-domain-model.md
2026-03-20 21:27:45 +01:00

6.8 KiB

Historical Stats Domain Model

Objective

Definir la fuente real y el modelo de dominio minimo para construir una capa historica propia sobre los 2 servidores reales de Comunidad Hispana, sin implementar todavia la ingesta completa ni comprometer una base de datos productiva distinta de la base local ya usada en desarrollo.

Real Historical Sources

Los 2 servidores reales ya exponen historico publico en:

  • https://scoreboard.comunidadhll.es/games
  • https://scoreboard.comunidadhll.es:5443/games

Observaciones verificadas sobre ambas fuentes:

  • ambas responden con una SPA publica titulada Hell Let Loose Stats
  • la descripcion publica indica Hell Let Loose Statistics and Game History
  • ambas cargan el mismo bundle frontend, por lo que la estructura funcional observable parece comun y cambia solo el dataset servido por cada dominio
  • en el bundle publico aparecen referencias a conceptos como games, players, kills, deaths, matches y server

Inferencia operativa:

  • la fuente real no debe modelarse como una tabla HTML estable
  • la primera opcion tecnica futura debe ser descubrir y consumir un endpoint estructurado del scoreboard si existe
  • si no existe un endpoint publico reutilizable, la alternativa seria un parser controlado de HTML renderizado o del payload que hidrate la SPA

Scope Of Historical Analytics Phase 1

La primera capacidad historica util del proyecto sera:

  • top kills de la ultima semana por servidor

Por tanto, la ingesta base debe capturar como minimo:

  • servidor
  • partida
  • fecha/hora de partida
  • duracion si esta disponible
  • mapa
  • modo
  • jugador
  • kills
  • muertes si estan disponibles

Proposed Domain Entities

historical_servers

Proposito: representar cada servidor real de la comunidad como origen de historico.

Campos minimos:

  • server_id
  • external_server_id
  • display_name
  • scoreboard_base_url
  • is_active

Notas:

  • esta entidad reutiliza la identidad ya establecida en servers cuando la implementacion llegue a persistencia
  • scoreboard_base_url permite mantener separado el origen historico por servidor sin hardcodear rutas dispersas

historical_matches

Proposito: representar una partida individual extraida del scoreboard.

Campos minimos:

  • match_id
  • server_id
  • source_match_ref
  • started_at
  • ended_at nullable
  • duration_seconds nullable
  • map_id
  • mode_id nullable
  • source_fingerprint

Notas:

  • source_match_ref debe conservar el identificador nativo del scoreboard si la fuente lo expone
  • source_fingerprint cubre el caso en que no exista un id perfecto y haya que deduplicar por combinacion de campos observables

historical_maps

Proposito: mantener identidad y nombre normalizado de mapa.

Campos minimos:

  • map_id
  • map_slug
  • display_name
  • raw_map_name

Notas:

  • el pipeline debe conservar el valor crudo y tambien un nombre normalizado
  • esto evita que los rankings historicos mezclen variantes tecnicas del mismo mapa

historical_modes

Proposito: normalizar el modo de partida si la fuente lo ofrece.

Campos minimos:

  • mode_id
  • mode_slug
  • display_name
  • raw_mode_name

historical_players

Proposito: mantener una identidad de jugador lo bastante estable para consultas agregadas.

Campos minimos:

  • player_id
  • identity_kind
  • canonical_name
  • last_seen_name
  • source_player_ref nullable
  • identity_fingerprint

Notas:

  • si el scoreboard no da un id perfecto, la identidad inicial puede basarse en una huella controlada por servidor y nombre normalizado
  • la identidad debe ser revisable porque el nombre visible puede cambiar

historical_player_participations

Proposito: representar que un jugador participo en una partida concreta.

Campos minimos:

  • participation_id
  • match_id
  • player_id
  • team nullable
  • joined_at nullable
  • left_at nullable

Notas:

  • esta capa separa la presencia del jugador de sus metricas
  • deja espacio para futuras metricas por equipo o duracion de participacion

historical_player_match_stats

Proposito: guardar las metricas observadas por jugador dentro de una partida.

Campos minimos:

  • participation_id
  • kills
  • deaths nullable
  • assists nullable
  • score nullable
  • rank_in_match nullable
  • captured_at

Notas:

  • kills es la metrica obligatoria de la primera fase
  • deaths se persiste solo si la fuente la ofrece de forma estable

Identity And Deduplication Strategy

Match identity

Orden preferido:

  1. source_match_ref del scoreboard si existe y es estable
  2. huella derivada de server_id + started_at + map + mode + duration

Regla:

  • no insertar una partida nueva si ya existe la misma referencia nativa o la misma huella derivada

Player identity

Orden preferido:

  1. source_player_ref del scoreboard si existe
  2. identidad derivada de server_id + normalized_player_name

Riesgo:

  • esto no resuelve por completo renombres o colisiones entre jugadores con el mismo nombre

Mitigacion inicial:

  • conservar siempre canonical_name, last_seen_name y la referencia nativa cuando exista
  • documentar que los rankings de fase 1 pueden tener precision limitada si el scoreboard no expone un id fuerte

Ingestion deduplication

Para poder reejecutar la ingesta sin duplicacion grave:

  • upsert por source_match_ref o source_fingerprint en partidas
  • upsert por source_player_ref o identity_fingerprint en jugadores
  • unicidad por match_id + player_id en participaciones
  • unicidad por participation_id en metricas de partida por jugador

Initial Query Shape

Consulta prioritaria:

  • top kills de la ultima semana por servidor

Agrupacion minima necesaria:

  • filtrar partidas del servidor cuya ventana temporal caiga dentro de los ultimos 7 dias segun started_at o ended_at
  • sumar kills por player_id
  • devolver nombre canonico del jugador, kills agregadas y rango

Definicion inicial de ventana:

  • ventana rodante de 7 dias hacia atras desde el momento de consulta
  • usar UTC en backend para evitar ambiguedades

Risks And Limits

  • la estructura visible hoy es la de una SPA; el contrato interno del scoreboard puede cambiar sin aviso
  • no se ha fijado todavia un endpoint JSON publico y estable del scoreboard
  • si solo existe HTML cliente, el scraping sera mas fragil que una API
  • la identidad de jugador puede ser imperfecta si la fuente no expone un id
  • una misma partida podria aparecer con informacion parcial durante una captura
  • la disponibilidad del scoreboard no debe bloquear el backend principal
  1. Descubrir primero el endpoint estructurado o payload interno usado por la SPA.
  2. Implementar un parser backend desacoplado por servidor de scoreboard.
  3. Persistir solo entidades y metricas necesarias para top kills semanal.
  4. Mantener la ingesta idempotente antes de abrir endpoints historicos nuevos.