About arc42

arc42, the template for documentation of software and system architecture.

Template Version 8.2 EN. (based upon AsciiDoc version), January 2023

Created, maintained and © by Dr. Peter Hruschka, Dr. Gernot Starke and contributors. See https://arc42.org.


Note

This version of the template contains some help and explanations. It is used for familiarization with arc42 and the understanding of the concepts. For documentation of your own system you use better the plain version.

1. Introduction and Goals

El proyecto Yovi es una propuesta planteada por la empresa de desarrollo de videojuegos Micrati, cuyo objetivo es desarrollar una aplicación web para jugar al juego Y, permitiendo tanto la interacción de usuarios humanos como la integración de bots mediante una API externa.

1.1. Participantes

El equipo está formado por:

  • Pablo García Menéndez, UO299834

  • Elías Fernández Medina, UO299673

  • Sergio Espina Marcos, UO301107

  • Adrián Pérez Menéndez, UO301188

1.2. Requirements Overview

Los requisitos del proyecto Yovi son los siguientes:

  • Los usuarios podrán jugar partidas desde la web al juego Y, al menos a la versión clásica del juego y contra una máquina.

  • Los usuarios podrán elegir el tamaño del tablero, así como la estrategia que el bot rival empleará.

  • Los usuarios podrán jugar partidas multijugador en tiempo real contra otros humanos, mediante salas identificadas por un código de 6 caracteres.

  • Durante las partidas multijugador existirá un chat en tiempo real entre los jugadores.

  • Los usuarios podrán registrarse en el sistema y consultar el histórico de su participación: número de juegos realizados, estadísticas de partidas ganadas/perdidas, racha actual, bots derrotados, etc.

  • La aplicación dispondrá de un ranking global (leaderboard) de jugadores ordenado por tasa de victorias.

  • La autenticación se realizará mediante tokens JWT, gestionados por un microservicio de autenticación dedicado.

  • La aplicación dispondrá de una API que permita a los bots interactuar con ella.

  • La API de la aplicación permitirá acceder y gestionar información de los usuarios y de las partidas.

  • La API tendrá documentación.

  • La API permitirá que un bot juegue contra la aplicación.

  • La aplicación dispondrá de monitorización de métricas mediante Prometheus y Grafana.

1.3. Quality Goals

Objetivo de calidad Descripción

Usabilidad

La aplicación web deberá ser sencilla de usar e intuitiva, permitiendo que cualquier usuario tenga una buena experiencia jugando al juego Y.

Rapidez

Se buscará que los tiempos de respuesta sean lo más cortos posibles, tanto durante la partida como en la navegación de la aplicación.

Mantenibilidad

La aplicación deberá ser fácil de mantener, con código limpio y fácil de modificar o ampliar, siguiendo buenas prácticas de diseño de software.

Fiabilidad

La aplicación tendrá que ser fiable y funcionar correctamente ante cualquier situación llevada a cabo por los usuarios. También deberá de estar disponible el mayor tiempo posible.

Seguridad

El acceso a los recursos protegidos requiere autenticación JWT. Las contraseñas se almacenan hasheadas con bcrypt. Ningún puerto interno está expuesto directamente al exterior.

1.4. Stakeholders

Rol/Nombre Contacto Expectativas

Micrati

Empresa de desarrollo de videojuegos

Espera una entrega de la aplicación que cumpla todos los requisitos especificados.

Equipo de desarrollo

Sergio Espina Marcos, Pablo García Menéndez, Elías Fernández Medina, Adrián Pérez Menéndez

Espera desarrollar una aplicación web aplicando y mejorando sus conocimientos y habilidades.

Usuarios

Personas que utilicen la aplicación

Esperan poder divertirse jugando al juego Y, tanto contra bots como contra otros humanos, y consultar sus estadísticas.

Bots externos

Scripts o programas que usen GET /play

Esperan una API estable y documentada para competir automáticamente.

Profesor

Jose Emilio Labra Gayo

Espera asistir al equipo de desarrollo en el proyecto y evaluarlo.

2. Architecture Constraints

2.1. Technical Constraints

Restricción Descripción

Lenguaje frontend

La aplicación web debe desarrollarse utilizando TypeScript con React.

Módulo de lógica

El motor del juego debe implementarse en lenguaje introducido en seminarios Rust.

Comunicación

La comunicación entre el frontend y el módulo de lógica se realizará mediante servicios web usando JSON.

Notación YEN

Las partidas deberán representarse utilizando la notación YEN proporcionada.

API externa

El sistema debe exponer una API que permita la interacción con bots.

Despliegue web

La aplicación debe estar accesible a través de Internet (p.e VM de Azure).

2.2. Organizational Constraints

Restricción Descripción

Trabajo en equipo

El desarrollo se realiza en equipo, lo cual implica coordinación y reparto de tareas equitativo.

Evaluación académica

El proyecto será evaluado según los criterios establecidos.

Uso de GitHub

El repositorio debe mantenerse actualizado con un historial claro de commits, issues, actas y decisiones que se puedan tomar.

2.3. Conventions

Convención Descripción

PLantilla arc42

Se seguirá la plantilla de arc42 para documentar la arquitectura.

Buenas prácticas

Se aplicarán principios de diseño limpio y mantenible.

Control de versiones

Se utilizará Git para el control de versiones.

3. Context and Scope

3.1. Business Context

Elemento Entrada Salida

Usuario humano

Accede a la aplicación, selecciona modo de juego, realiza movimientos, consulta historial

Visualización del tablero, resultado de la partida, historial de partidas

Jugador multijugador (sala)

Crea o se une a una sala mediante código de 6 caracteres; envía movimientos y mensajes de chat

Actualizaciones del tablero en tiempo real, mensajes de chat del oponente

Bot externo (competición)

Envía estado YEN en formato JSON al endpoint GET /play?position=<yen>&bot_id=<bot_id>

Estado del tablero tras la jugada del bot oponente, resultado de la partida

Sistema Yovi

Recibe movimientos, peticiones de estado y mensajes de sala

Gestiona partidas, usuarios, salas WebSocket y lógica del juego

Módulo de lógica (Rust/Gamey)

Estado del tablero en formato YEN + coordenadas de la jugada

Nuevo estado YEN validado, status de la partida, jugada del bot

Gateway (Spring Boot)

Peticiones HTTP del frontend o bots externos

Redirige al microservicio correspondiente (users o gamey)

Nginx proxy

Recibe peticiones HTTPS del frontend

Transforma y envia las peticiones HTTPS en HTTP al backend

Servidor WebSocket (ws-server)

Mensajes de sala: create, join, board_update, game_over, chat

Empareja jugadores, retransmite movimientos y chat entre oponentes

El sistema Yovi actúa como intermediario entre los usuarios, los bots externos y el módulo de lógica del juego. Los usuarios interactúan a través de la interfaz web mediante HTTP y WebSocket, mientras que los bots lo hacen mediante el endpoint REST GET /play.


3.2. Technical Context

Elemento Canal Tecnología

Usuario ↔ WebApp (SPA)

Navegador web

HTTP/HTTPS, React + TypeScript + Material UI

WebApp ↔ Gateway (REST)

Peticiones HTTP/HTTPS

JSON, puerto 80 (Nginx proxy → gateway:8080)

WebApp ↔ Servidor WebSocket

Conexión WebSocket persistente

ws:// o wss://, protocolo de salas propio, puerto 80 (Nginx proxy → ws-server:8081)

Gateway ↔ Users Service

Red interna Docker, HTTP

JSON, puerto 3000

Gateway ↔ Gamey Engine

Red interna Docker, HTTP

JSON + notación YEN, puerto 4000

Users Service ↔ MySQL

Red interna Docker, TCP

SQL, puerto 3306

Bot externo ↔ Gateway

Peticiones HTTP

JSON, GET /play, puerto 80


3.3. Mapping Input/Output to Channels

Entrada/Salida Canal

Movimiento del usuario (vs bot)

HTTPS → Nginx proxy → HTTP → Gateway → Gamey Engine (/v1/game/play)

Movimiento del usuario (multijugador)

HTTPS → Nginx proxy → HTTP → Gateway → Gamey Engine + WebSocket broadcast al oponente

Visualización del tablero

Renderizado SVG en la SPA (estado recibido de Gamey)

Petición de jugada del bot interno

HTTP → Gateway → Gamey Engine (/v1/ybot/choose/{botId})

Petición de partida bot-vs-bot

HTTP → Gateway → Users Service → Gamey Engine (GET /play)

Chat en sala multijugador

WebSocket bidireccional (cliente ↔ ws-server ↔ oponente)

Guardado de partida

HTTPS → Nginx proxy → → Gateway → Users Service → MySQL

Historial de partidas

HTTPS → Nginx proxy → → Gateway → Users Service → MySQL

Registro/login de usuario

HTTPS → Nginx proxy → → Gateway → Users Service → MySQL

4. Solution Strategy

El sistema Yovi se ha diseñado siguiendo principios de modularidad y separación de responsabilidades. La estrategia general se basa en los siguientes aspectos:

4.1. Top-Level Decomposition

  • Frontend web (TypeScript/React): Se encarga de la interacción con los usuarios y la presentación del tablero de juego. Incluye selector de modo, partida vs bot, lobby multijugador con chat, historial, leaderboard y perfil de usuario.

  • Microservicio de autenticación (auth-service, Node.js): Centraliza el login y el registro. Verifica credenciales contra el Users Service y emite tokens JWT firmados. Desacopla la lógica de autenticación del resto del sistema.

  • Gateway (Spring Boot): Punto de entrada HTTP único para el frontend y los bots externos. Valida el token JWT de cada petición y enruta al servicio interno correcto. Las rutas públicas (/auth/, /status, /actuator/) no requieren token.

  • Users Service (Node.js/Express): API REST que gestiona usuarios, partidas, estadísticas y el endpoint de competición GET /play. Accede a MySQL para persistencia.

  • Módulo de lógica del juego (Rust/Gamey): Calcula el estado de la partida, determina movimientos y comprueba victorias. Se comunica mediante JSON utilizando la notación YEN.

  • Servidor WebSocket (ws-server, Node.js): Gestiona salas multijugador en tiempo real. No contiene lógica de juego; solo empareja jugadores y retransmite mensajes.

  • Nginx: Proxy inverso y punto de entrada único (puerto 80/443). Sirve los estáticos de React y hace proxy HTTP al gateway y WebSocket al ws-server.

  • Persistencia de datos (MySQL): Registro de usuarios, partidas e historial.

  • Monitorización (Prometheus + Grafana): Recogida y visualización de métricas del Users Service.


4.2. Technology Decisions

  • Se utiliza TypeScript con React para la web por su tipado estático y facilidad de mantenimiento.

  • Se utiliza Rust para la lógica del juego por su rendimiento y seguridad.

  • Se usa JSON + notación YEN para comunicar los módulos y con los bots.

  • Se usa JWT (JSON Web Tokens) para la autenticación stateless: el token se firma en el auth-service y se valida en el gateway sin necesidad de consultar la base de datos en cada petición.

  • La API está basada en servicios web simples (HTTP) para facilitar la integración con bots y otros sistemas externos.

  • Se usa WebSocket puro (ws library) para la comunicación en tiempo real del modo multijugador y el chat, por su simplicidad y bajo overhead.

  • Se emplean Prometheus y Grafana para monitorización, integrados con el Users Service.

  • Se emplea Git con GitHub para control de versiones y seguimiento del proyecto.

  • Docker Compose orquesta todos los servicios tanto en desarrollo como en producción.


4.3. Key Quality Goals

  • Usabilidad: Se prioriza una interfaz clara e intuitiva para el usuario.

  • Rendimiento: Respuesta rápida durante la partida y consultas al API. La validación JWT en el gateway es local (sin roundtrip adicional a la BD).

  • Seguridad: JWT para autenticación stateless, bcrypt para contraseñas, Nginx como único punto de entrada exterior.

  • Mantenibilidad: Código poco acoplado, cohesivo, modular y limpio que permite añadir estrategias o variantes del juego sin romper el sistema. Cobertura de tests medida con SonarCloud.


4.4. Group Decisions

  • El proyecto se desarrolla de forma colaborativa por el equipo indicado, con coordinación mediante reuniones telemáticas y/o presenciales, contacto por WhatsApp y el uso de GitHub.

  • Se sigue una metodología ágil ligera de tipo SCRUM como la aprendida en asignaturas pasadas como IPS.

  • La documentación sigue la plantilla arc42, pues es la indicada por el profesor.

5. Building Block View

5.1. Whitebox Overall System

Diagrama de bloques del sistema completo

building block overview
Motivation

El sistema se descompone en microservicios independientes para separar la autenticación, la presentación, la gestión de usuarios/partidas, la lógica del juego y la comunicación en tiempo real. Nginx actúa como punto de entrada único en producción, eliminando la exposición de puertos internos al exterior y habilitando el proxy WebSocket. El auth-service desacopla la lógica de JWT del gateway y del users service.

Contained Building Blocks
Bloque Responsabilidad

WebApp

SPA que proporciona la interfaz de usuario: selección de modo, tablero de juego, historial, multijugador con chat, leaderboard y perfil.

Nginx

Servidor web y proxy inverso: sirve los estáticos de React, redirige /auth/* al auth-service, HTTP al gateway y WebSocket al ws-server. TLS en producción.

Auth Service

Microservicio de autenticación JWT. Expone POST /auth/login y POST /auth/register. Verifica credenciales contra users-service y emite tokens JWT firmados.

Gateway

Punto de entrada HTTP único con JWT validation. Valida el token Authorization: Bearer <token> en cada petición y enruta al servicio correcto (users o gamey). Las rutas /auth/, /status y /actuator/ son públicas.

Users Service

API REST de usuarios y partidas. Gestiona registro, login, guardado de partidas, historial, estadísticas de usuario (/api/users/:id/stats), leaderboard (/api/leaderboard) y el endpoint de competición GET /play.

Gamey Engine

Motor de juego en Rust. Valida movimientos, comprueba victorias y ejecuta los algoritmos de bot.

WS-Server

Servidor WebSocket de salas multijugador. Empareja jugadores por código, retransmite movimientos y chat.

MySQL

Persistencia de usuarios, partidas y jugadores.

Prometheus

Recogida de métricas del Users Service.

Grafana

Visualización de dashboards de métricas.

Important Interfaces
  • HTTPS/JSON entre WebApp y Auth Service (puerto 443/80, rutas /auth/*, sin JWT).

  • HTTPS/JSON entre WebApp y Gateway (puerto 443/80, rutas /api/, /v1/; requiere Authorization: Bearer <token>).

  • WebSocket wss://<host>/ws entre WebApp y WS-Server (Nginx proxy).

  • HTTP/JSON interno entre Gateway y Users Service (puerto 3000).

  • HTTP/JSON interno entre Gateway y Gamey Engine (puerto 4000).

  • HTTP/JSON interno entre Users Service y Gamey Engine (para GET /play).

  • SQL entre Users Service y MySQL (puerto 3306).


5.2. Level 2 — Bloques internos relevantes

5.2.1. White Box Auth Service

Microservicio Node.js responsable exclusivamente de la autenticación. No accede a MySQL directamente; delega la verificación de credenciales en Users Service.

Endpoint Responsabilidad

POST /auth/login

Valida credenciales contra users-service. Si son correctas, firma y devuelve un JWT con userId y username. Expira según JWT_EXPIRES_IN (por defecto 1h).

POST /auth/register

Proxy directo a /createuser de users-service. Crea el usuario. El cliente realiza login automáticamente tras el registro para obtener el token.

GET /auth/validate

Verifica un JWT y devuelve su payload. Útil para tests e integraciones internas.

GET /auth/health

Health check del servicio.

5.2.2. White Box Gateway (JWT Filter)

El Gateway aplica un filtro Servlet (JwtFilter) en todas las rutas salvo las públicas.

Comportamiento Descripción

Rutas públicas

/auth/, /actuator/, /status — pasan sin token.

Rutas protegidas

Todas las demás. El filtro extrae el token del header Authorization: Bearer <token>, lo verifica con JWT_SECRET y añade X-User-Id al request forwarded.

Token inválido

Devuelve 401 Unauthorized con mensaje de error JSON.

Rutas que expone el gateway:

Método Ruta Destino

GET

/api/games

Users Service

POST

/api/games

Users Service

POST

/api/games/seed

Users Service

GET

/api/users/{userId}/stats

Users Service

GET

/api/leaderboard

Users Service

GET

/play

Users Service

GET

/status

Gamey Engine (público)

POST

/v1/ybot/choose/{botId}

Gamey Engine

POST

/v1/game/play

Gamey Engine

5.2.3. White Box WebApp

La WebApp sigue la arquitectura SPA con componentes React organizados por funcionalidad.

Componente Responsabilidad

App.tsx

Punto de entrada: gestión de autenticación y navegación entre vistas.

GameModeSelector

Pantalla previa al juego: elige modo (bot/multijugador), tamaño de tablero, dificultad y quién empieza.

GameView

Orquestador de modos: transición entre selector → bot game / mp-lobby → mp-game.

Game

Partida vs bot: alterna turno humano y turno bot llamando a la API Rust.

HexBoard

Tablero SVG hexagonal (flat-top, pirámide centrada). Geometría exportada y compartida con GameHistory.

MultiplayerLobby

Sala de espera: crear sala (obtiene código de 6 chars) o unirse con código.

MultiplayerGame

Partida multijugador: tablero + chat lateral. Movimientos vía API Rust + broadcast WebSocket.

GameHistory

Historial de partidas con miniatura del tablero.

LeaderboardView

Ranking global paginado, ordenado por tasa de victorias.

ProfileView

Estadísticas del usuario: partidas, victorias, derrotas, tasa, racha, bots derrotados.

useWebSocketRoom

Hook que encapsula todo el protocolo WebSocket: conexión, sala, mensajes, estado.

gameyClient.ts

Cliente HTTP para la API Rust: makeHumanMove, chooseMove.

5.2.4. White Box Users Service

API REST basada en Express con acceso directo a MySQL y llamadas HTTP internas a Gamey.

Endpoint Responsabilidad

POST /createuser

Registro de usuario con contraseña hasheada (bcrypt, 10 rounds).

POST /login

Autenticación interna (usado por auth-service). Devuelve userId.

GET /api/games

Lista todas las partidas con sus jugadores.

POST /api/games

Guarda una partida finalizada en MySQL.

POST /api/games/seed

Inserta partidas de ejemplo.

GET /api/users/:userId/stats

Estadísticas completas del usuario: partidas, victorias, derrotas, tasa de victorias, racha, top día y bots derrotados.

GET /api/leaderboard

Ranking paginado de usuarios ordenado por tasa de victorias (parámetros: limit, offset).

GET /play

Endpoint de competición: recibe el estado YEN actual como query param (position) y el bot_id opcional. Pide al bot que elija su jugada y devuelve las coordenadas elegidas. Parámetros: position (JSON YEN), bot_id (por defecto random_bot).

5.2.5. White Box Gamey Engine

Proyecto en Rust con estructura modular expuesto como servidor HTTP con Axum.

Módulo Responsabilidad

core/

Reglas del juego, estado YEN, validación de movimientos, detección de victoria.

bot/

Implementaciones de bots: random_bot, greedy_bot, minimax_bot.

bot_server/play.rs

Handler POST /v1/game/play: valida y aplica un movimiento.

bot_server/choose.rs

Handler POST /v1/ybot/choose/{botId}: el bot elige su jugada.

5.2.6. White Box WS-Server

Servidor Node.js con la librería ws. No contiene lógica de juego.

Función Responsabilidad

handleCreate

Crea sala con código único de 6 caracteres alfanuméricos. El creador espera.

handleJoin

Une al segundo jugador. Asigna playerIndex aleatoriamente (0=Azul, 1=Rojo).

handleForward

Retransmite board_update, game_over y chat entre los dos jugadores de la sala.

handleClose

Notifica al oponente si un jugador se desconecta. Limpia la sala.


5.3. Level 3 — Detalles de implementación relevantes

5.3.1. Flujo de autenticación JWT

jwt flow

5.3.2. Geometría del tablero hexagonal — buildHexGeometry (HexBoard.tsx)

Función exportada que calcula las posiciones de todas las celdas del tablero triangular. Usa hexágonos flat-top (lados planos arriba/abajo) en forma de pirámide centrada:

colStep  = √3 · r                          // separación horizontal
rowStep  = 1.5 · r                         // separación vertical
offsetX(row) = (n - 1 - row) · colStep / 2 // centrado por fila
cx(row, col) = offsetX(row) + col · colStep
cy(row)      = row · rowStep

La misma función es importada por GameHistory.tsx para garantizar que el miniboard del historial es geométricamente idéntico al tablero principal.

5.3.3. GET /play — Flujo interno

get play flow

6. Runtime View

6.1. Login con JWT

Caso

El usuario introduce sus credenciales. El auth-service las verifica y emite un token JWT que el frontend usa en todas las peticiones posteriores.

Login JWT

6.2. Registro del usuario

Caso

El usuario accede a la página web e inicia el proceso de registro.

Registro de Usuario

6.3. Partida vs Bot

Caso

El usuario selecciona el modo vs bot, configura tamaño, dificultad y quién empieza, y juega una partida completa.

Partida vs Bot

6.4. Partida Multijugador (WebSocket)

Caso

Dos jugadores crean y se unen a una sala, juegan una partida con chat en tiempo real.

Partida Multijugador

6.5. Desconexión de un jugador en partida multijugador

Caso

Un jugador cierra el navegador o pierde la conexión durante una partida. El sistema notifica al oponente.

Desconexion Multijugador

6.6. Consulta del Leaderboard

Caso

El usuario navega al ranking global para ver la clasificación de jugadores.

Leaderboard

6.7. Consulta del Perfil de Usuario

Caso

El usuario navega a su perfil para ver sus estadísticas detalladas.

Perfil Usuario

6.8. Competición Bot vs Bot (GET /play)

Caso

Un bot externo (o script de testing) quiere conocer la jugada que elegiría el bot de la aplicación dado un estado de tablero.

Bot vs Bot

6.9. Fin de partida vs Bot — Experiencia del usuario

Caso

La partida termina y el usuario decide si jugar de nuevo o ver el historial.

  • La partida se guarda en MySQL en background (el usuario no espera).

  • Se muestran simultáneamente dos botones:

    • "Jugar de nuevo": reinicia el tablero con la misma configuración sin volver al menú.

    • "Volver al menú": navega al GameModeSelector para elegir nueva configuración.

  • El historial de partidas se refresca automáticamente cuando el usuario navega a esa vista.

7. Deployment View

7.1. Infrastructure Level 1

deployment overview
Motivation

Todo el tráfico exterior entra por el puerto 80 (Nginx), que actúa como proxy inverso. Solo Nginx está expuesto al exterior; el resto de servicios se comunican por la red interna de Docker. Esto simplifica la configuración de firewall, evita problemas de CORS y habilita el proxy WebSocket.

Puertos expuestos al exterior
Puerto Servicio Descripción

80

Nginx (webapp)

SPA React + proxy HTTP y WebSocket

9090

Prometheus

Métricas (solo acceso interno/admin)

9091

Grafana

Dashboards de monitorización

3306

MySQL

Acceso directo a BD (solo desarrollo local)

Mapping Building Blocks → Infrastructure
Bloque Contenedor Docker

WebApp (React + Nginx)

webapp — imagen multi-stage Node+Nginx

Gateway

gateway — Spring Boot JAR en JRE 21

Users Service

users — Node.js 22

Gamey Engine

gamey — binario Rust compilado en Alpine

WS-Server

ws-server — Node.js 20 Alpine

MySQL

mysql — imagen oficial MySQL 8.0


7.2. Infrastructure Level 2 — Nginx como proxy

Nginx Routing

La configuración del proxy WebSocket en Nginx requiere los headers especiales:

location /ws {
    proxy_pass         http://ws-server:8081;
    proxy_http_version 1.1;
    proxy_set_header   Upgrade    $http_upgrade;
    proxy_set_header   Connection "Upgrade";
    proxy_read_timeout 3600s;
}

Sin estos headers, Nginx no haría el handshake HTTP→WebSocket y la conexión fallaría.


7.3. Infrastructure Level 2 — Variables de entorno por servicio

Servicio Variable Valor en Docker

gateway

USERS_SERVICE_URL

http://users:3000

gateway

GAMEY_SERVICE_URL

http://gamey:4000

users

DB_HOST

mysql

users

DB_PORT

3306

users

GAMEY_SERVICE_URL

http://gamey:4000

webapp (build)

VITE_API_URL

http://localhost:80

webapp (build)

VITE_GAMEY_URL

http://localhost:80

webapp (build)

VITE_WS_URL

ws://localhost:80/ws

ws-server

WS_PORT

8081

8. Cross-cutting Concepts

8.1. Modelo de Dominio

El sistema sigue un modelo de dominio centrado en la separación de responsabilidades:

  • Usuarios: gestión de cuentas, autenticación y autorización (Auth Service + Users Service).

  • Partidas: lógica de reglas, estado YEN, bots y validación de movimientos (Gamey Engine).

  • Salas multijugador: emparejamiento, sincronización en tiempo real y chat (WS-Server).

  • Interacción Web: comunicación entre frontend y backend mediante HTTP REST y WebSocket.

  • Monitorización: recogida y visualización de métricas del sistema (Prometheus + Grafana).

8.2. Autenticación y Autorización — JWT

El sistema usa JSON Web Tokens (JWT) para autenticación stateless.

Flujo
  1. El cliente envía credenciales a POST /auth/login (ruta pública, sin token).

  2. El Auth Service verifica contra Users Service y firma un JWT con { userId, username } usando JWT_SECRET.

  3. El cliente incluye el token en todas las peticiones posteriores: Authorization: Bearer <token>.

  4. El Gateway valida el token en su JwtFilter (verificación local, sin roundtrip a BD) y añade X-User-Id al request.

  5. Las rutas /auth/, /status y /actuator/ son públicas y no requieren token.

Ventajas del enfoque
  • Stateless: el gateway no necesita consultar la BD para validar cada petición.

  • Desacoplado: el Auth Service puede evolucionar (añadir OAuth, MFA) sin cambiar el gateway.

  • Seguridad: el token expira (por defecto 1h); si se compromete, la ventana de ataque es limitada.

8.3. Notación YEN

El estado de todas las partidas se representa mediante la notación YEN (formato JSON):

{
  "size":    5,
  "turn":    0,
  "players": ["B", "R"],
  "layout":  "./../.../..../....."
}
  • layout: filas separadas por /, cada carácter es . (vacío), B (Azul) o R (Rojo).

  • turn: índice del jugador cuyo turno es (0 o 1).

  • players: identificadores de los jugadores en orden.

8.4. Comunicación en Tiempo Real — WebSocket de Salas

El módulo ws-server implementa un protocolo de salas propio sobre WebSocket puro.

Principio de diseño clave: el servidor WS no contiene lógica de juego. Solo empareja jugadores y retransmite mensajes. La validación de movimientos siempre la realiza la API Rust (/v1/game/play). Esto garantiza que la lógica del juego sea la misma para todos los modos (bot y multijugador).

Protocolo de mensajes
Tipo Dirección Descripción

create

Cliente → Servidor

Crea sala nueva. Recibe roomCode de 6 chars.

join

Cliente → Servidor

Se une a sala existente por roomCode.

room_created

Servidor → Cliente

Confirmación de sala creada con roomCode.

game_start

Servidor → Ambos

La sala está llena. Incluye playerIndex asignado aleatoriamente.

board_update

Cliente → Servidor → Oponente

Nuevo estado del tablero tras un movimiento.

game_over

Cliente → Servidor → Oponente

Partida terminada con el ganador.

chat

Cliente → Servidor → Oponente

Mensaje de texto. El servidor añade el campo from.

error

Servidor → Cliente

Error de sala, conexión perdida o desconexión del oponente.

Asignación aleatoria de color

Cuando el segundo jugador se une a la sala, el servidor asigna los índices (0=Azul, 1=Rojo) aleatoriamente mediante Math.random() < 0.5. Esto garantiza equidad en partidas entre humanos.

8.5. Geometría del Tablero Hexagonal

El tablero usa hexágonos flat-top (lados planos arriba/abajo, vértices a izquierda y derecha) en forma de pirámide centrada: la fila 0 tiene 1 celda arriba y la fila n-1 tiene n celdas abajo.

La fórmula de centrado por fila:

offsetX(row) = (n - 1 - row) × (√3 · r) / 2
cx(row, col) = offsetX(row) + col × √3 · r
cy(row)      = row × 1.5 · r

La función buildHexGeometry y flatHexPoints se exportan desde HexBoard.tsx y son reutilizadas por GameHistory.tsx (miniaturas del historial), garantizando que ambas representaciones son geométricamente idénticas.

8.6. API de Competición — GET /play

El endpoint GET /play permite a bots externos consultar qué jugada elegiría el bot de la aplicación dado un estado de tablero en formato YEN. El cliente envía el estado actual como query parameter y recibe las coordenadas elegidas por el bot.

Internamente realiza una sola llamada a Gamey Engine:

  1. POST /v1/ybot/choose/{bot_id} → el bot elige su jugada y devuelve { coords }.

Parámetros:

  • position (obligatorio): estado del tablero en formato YEN serializado como JSON string.

  • bot_id (opcional, por defecto random_bot): identificador del bot a usar.

Bots disponibles: random_bot (aleatorio), greedy_bot (codicioso), minimax_bot (minimax profundidad 2).

8.7. Monitorización — Prometheus y Grafana

El Users Service expone métricas compatibles con Prometheus. La pila de monitorización funciona así:

  • Prometheus hace scraping del endpoint de métricas del Users Service periódicamente y almacena las series temporales.

  • Grafana se conecta a Prometheus como datasource y visualiza los datos en dashboards preconfigurados.

  • Ambos servicios están incluidos en el docker-compose.yml y se levantan automáticamente.

    Puertos de acceso

8.8. Experiencia de Usuario al Finalizar Partida

Al terminar una partida vs bot, el tablero permanece visible con el resultado. El guardado en la base de datos se realiza en background (sin await bloqueante), de forma que la UI responde inmediatamente. El jugador ve dos botones:

  • "Jugar de nuevo": reinicia el tablero con la misma configuración (mismo tamaño, dificultad y quién empieza) sin volver al menú.

  • "Volver al menú": navega al GameModeSelector para elegir nueva configuración o modo.

8.9. Experiencia de Usuario (UX)

  • La interfaz sigue el patrón SPA para ofrecer fluidez y rapidez.

  • Selección de modo de juego previa a la partida: modo (bot/multijugador), tamaño del tablero (mínimo 5), dificultad del bot y elección de quién empieza.

  • En modo multijugador: pantalla de lobby con pestañas "Crear sala" / "Unirse a sala", con código de 6 caracteres y chat disponible durante la partida.

  • Leaderboard global paginado accesible desde la NavBar.

  • Perfil de usuario con estadísticas detalladas: tasa de victorias, racha, bots derrotados, top día y fecha de registro.

  • Mensajes de error y estado consistentes en toda la aplicación.

8.10. Seguridad y Autenticación

  • JWT: tokens firmados con JWT_SECRET compartido entre auth-service y gateway. Expiración configurable (por defecto 1h).

  • bcrypt: contraseñas hasheadas con 10 salt rounds en Users Service. Nunca se almacenan en texto plano.

  • Nginx como punto de entrada único: los puertos internos no están expuestos al exterior.

  • Rutas públicas mínimas: solo /auth/, /status y /actuator/ son accesibles sin token.

  • El servidor WebSocket no requiere autenticación formal (partidas por código de sala).

8.11. Patrones de Arquitectura y Diseño

  • Microservicios: cada servicio tiene una responsabilidad clara y se despliega en un contenedor Docker independiente.

  • Proxy inverso (Nginx): punto de entrada único para HTTP y WebSocket.

  • Gateway pattern con JWT filter: el gateway de Spring Boot valida tokens y enruta peticiones REST al servicio correcto.

  • Auth Service desacoplado: la emisión y verificación de tokens está aislada del resto de la lógica de negocio.

  • Hook de estado (useWebSocketRoom): encapsula todo el ciclo de vida del WebSocket en un hook React reutilizable.

  • Geometría compartida: buildHexGeometry exportada para reutilización sin duplicar código.

8.12. Conceptos de Desarrollo

  • TypeScript estricto en el frontend con tipos compartidos entre componentes.

  • Tests unitarios con Vitest + Testing Library (frontend) y Vitest + Supertest (users service).

  • Tests de integración y benchmarks en Rust (cargo test, cargo bench).

  • Tests E2E con Playwright + Cucumber (BDD). Las APIs externas se mockean con page.route() y page.addInitScript() (WebSocket falso inyectado en el navegador). Los escenarios cubren: registro, login, partida vs bot, lobby multijugador, flujo WebSocket completo (crear sala, unirse, movimientos, chat, errores), leaderboard y perfil.

  • Cobertura de código reportada a SonarCloud y visible mediante badge en el README.

8.13. Conceptos Operativos

  • Docker Compose como orquestador en desarrollo y producción.

  • Variables de entorno para configurar URLs entre servicios y secretos (JWT_SECRET, DB_PASS) sin hardcodear valores.

  • Prometheus + Grafana para monitorización de métricas del Users Service.

  • Logs locales por servicio; integrables con sistemas centralizados en el futuro.

  • El timeout del proxy WebSocket en Nginx se configura a 3600s para mantener partidas largas activas.

  • CI/CD con GitHub Actions: en cada push se ejecutan tests y se publica la imagen Docker en GHCR.

9. Architecture Decisions

9.1. MySQL como BBDD del proyecto

Estado

Aceptada.

Contexto

Se requiere un sistema de persistencia para almacenar los datos de los usuarios, partidas y jugadores.

Justificación

Modelo relacional

MySQL permite estructurar los datos en tablas normalizadas (users, games, game_players), ideal para las relaciones entre entidades del juego.

Integridad de datos

Claves foráneas entre game_players y games/users garantizan consistencia.

Madurez y soporte

Tecnología estable, ampliamente documentada y compatible con Node.js (mysql2/promise).

Adopción del equipo

Tecnología conocida por todos los miembros del equipo.

Facilidad de despliegue

Compatible con Docker; imagen oficial mysql:8.0.

Consecuencias
  • Esquema relacional que requiere planificación previa de tablas y relaciones.

  • Cambios estructurales requieren migraciones. Se mitiga con CREATE TABLE IF NOT EXISTS en el arranque.


9.2. Azure como infraestructura de despliegue

Estado

Aceptada.

Contexto

Se requiere un entorno cloud para desplegar la aplicación con acceso desde Internet.

Justificación

Adopción del equipo

Tecnología conocida por todos los miembros del equipo.

IaaS con control total

Máquina virtual Linux con Docker: control completo del entorno.

Escalabilidad

Posibilidad de ajustar recursos (CPU, RAM) según necesidad.

Integración CI/CD

Compatible con GitHub Actions para despliegue automático.


9.3. Nginx como proxy inverso único (puerto 80/443)

Estado

Aceptada.

Contexto

La webapp está empaquetada como archivos estáticos (Vite build) que necesitan ser servidos. El frontend necesita comunicarse con el auth-service, el gateway (HTTP) y el servidor WebSocket (WS) sin exponer puertos internos al exterior.

Alternativas consideradas
  • Exponer directamente los puertos de gateway (8080) y ws-server (8081) al cliente.

  • Usar un API Gateway externo (Kong, Traefik).

Justificación

Punto de entrada único

El cliente solo necesita conocer un host y puerto (80/443). Simplifica configuración y firewall.

Proxy WebSocket

Nginx maneja el handshake HTTP→WebSocket con los headers Upgrade y Connection necesarios. Un proxy HTTP simple no sirve.

Sirve estáticos

Nginx sirve el build de React directamente, sin necesidad de un servidor Node.js adicional en producción.

CORS

Al servir todo desde el mismo origen, se eliminan los problemas de CORS entre frontend y APIs.

Sin coste adicional

Nginx está incluido en la imagen nginx:stable-alpine.

Consecuencias
  • Se añade un archivo nginx.conf que debe mantenerse actualizado si se añaden nuevas rutas.

  • El Dockerfile de la webapp usa una imagen multi-stage (Node para el build + Nginx para servir).


9.4. Microservicio de autenticación dedicado (auth-service)

Estado

Aceptada.

Contexto

El sistema necesita autenticación JWT. Las opciones eran integrar la lógica JWT en el gateway (Java), en el users-service (Node.js) o crear un microservicio dedicado.

Alternativas consideradas
  • Implementar login/JWT directamente en el gateway de Spring Boot.

  • Implementar login/JWT en el users-service junto con el resto de lógica de usuarios.

  • Microservicio dedicado en Node.js.

Justificación

Separación de responsabilidades

El auth-service solo se encarga de emitir y verificar tokens. El users-service solo gestiona datos. El gateway solo enruta.

Independencia tecnológica

Node.js es más ágil para un servicio simple de login que añadir lógica JWT compleja al gateway Spring Boot.

Escalabilidad

El auth-service puede escalarse independientemente si el volumen de logins crece.

Facilidad de evolución

Añadir OAuth2, MFA o SSO en el futuro requiere modificar solo el auth-service, sin tocar el gateway ni users-service.

Verificación local en gateway

El gateway valida JWT localmente con JWT_SECRET compartido; no necesita llamar al auth-service en cada petición (sin roundtrip adicional).

Consecuencias

9.5. Servidor WebSocket independiente (ws-server)

Estado

Aceptada.

Contexto

El modo multijugador requiere comunicación bidireccional en tiempo real entre dos clientes. El gateway (Spring Boot con MVC bloqueante) no soporta WebSocket de forma nativa sin cambios importantes.

Alternativas consideradas
  • Añadir WebSocket al gateway (requeriría migrar a WebFlux/reactive).

  • Añadir WebSocket al servicio de usuarios (mezcla responsabilidades).

  • Servidor WebSocket dedicado.

Justificación

Separación de responsabilidades

El servidor WS solo gestiona salas y retransmite mensajes; no contiene lógica de juego.

Simplicidad

La librería ws de Node.js es mínima y directa para este caso de uso.

No modifica el gateway

El gateway no necesita cambios; Nginx gestiona el routing al puerto 8081.

Sin lógica de juego

Los movimientos siempre pasan por la API Rust, garantizando validación centralizada.

Consecuencias

9.6. Endpoint GET /play en Users Service

Estado

Aceptada.

Contexto

Se necesita un endpoint que permita a bots externos consultar qué jugada elegiría el bot de la aplicación dado un estado de tablero en formato YEN.

Alternativas consideradas
  • Implementarlo directamente en el gateway (Java).

  • Crearlo como endpoint separado en un nuevo microservicio.

  • Exponerlo directamente desde Gamey Engine sin pasar por Users Service.

Justificación

Centralización

Users Service ya gestiona toda la lógica de negocio y tiene acceso a Gamey Engine mediante GAMEY_SERVICE_URL.

Simplicidad

El endpoint es un proxy ligero: parsea el YEN del query param y delega en Gamey Engine. Una sola llamada interna.

No rompe el gateway

El gateway solo añade el proxy de GET /play → users service, sin lógica nueva.

Interfaz simple para bots

El cliente externo hace una llamada GET con query params y recibe las coordenadas elegidas por el bot. Sin body, sin estado.

Consecuencias
  • Users Service realiza llamadas HTTP salientes a Gamey Engine (requiere GAMEY_SERVICE_URL).

  • Si Gamey Engine no está disponible, el endpoint devuelve 502.

10. Quality Requirements

Content

This section contains all quality requirements as quality tree with scenarios. The most important ones have already been described in section 1.2. (quality goals)

Here you can also capture quality requirements with lesser priority, which will not create high risks when they are not fully achieved.

Motivation

Since quality requirements will have a lot of influence on architectural decisions you should know for every stakeholder what is really important to them, concrete and measurable.

Further Information

See Quality Requirements in the arc42 documentation.

10.1. Quality Tree

quality tree
Content

The quality tree (as defined in ATAM – Architecture Tradeoff Analysis Method) with quality/evaluation scenarios as leafs.

Motivation

The tree structure with priorities provides an overview for a sometimes large number of quality requirements.

Form

The quality tree is a high-level overview of the quality goals and requirements:

  • tree-like refinement of the term "quality". Use "quality" or "usefulness" as a root

  • a mind map with quality categories as main branches

In any case the tree should include links to the scenarios of the following section.

10.2. Quality Scenarios

Contents

Concretization of (sometimes vague or implicit) quality requirements using (quality) scenarios.

These scenarios describe what should happen when a stimulus arrives at the system.

For architects, two kinds of scenarios are important:

  • Usage scenarios (also called application scenarios or use case scenarios) describe the system’s runtime reaction to a certain stimulus. This also includes scenarios that describe the system’s efficiency or performance. Example: The system reacts to a user’s request within one second.

  • Change scenarios describe a modification of the system or of its immediate environment. Example: Additional functionality is implemented or requirements for a quality attribute change.

Motivation

Scenarios make quality requirements concrete and allow to more easily measure or decide whether they are fulfilled.

Especially when you want to assess your architecture using methods like ATAM you need to describe your quality goals (from section 1.2) more precisely down to a level of scenarios that can be discussed and evaluated.

Form

Tabular or free form text.

Atributo Escenario Comportamiento esperado

Usabilidad

Un usuario accede por primera vez a la aplicación, sin conocimiento de la misma ni del juego, y comienza una partida.

El usuario entiende las reglas básicas y puede iniciar y jugar una partida sin necesidad de instrucciones externas.

Rendimiento

Un jugador realiza una acción durante la partida (por ejemplo, realizar un movimiento).

El sistema procesa la acción y actualiza el estado del juego inmediatamente o en un muy corto periodo de tiempo.

Mantenibilidad

Un desarrollador necesita añadir una nueva funcionalidad o modificar una regla del juego.

El cambio puede implementarse sin afectar a otras partes del sistema y sin introducir errores colaterales.

Fiabilidad

Durante una partida, el sistema enfrenta situaciones inesperadas (por ejemplo, entrada inválida, pérdida de conexión o error interno).

El sistema maneja el error de forma controlada, no se bloquea ni pierde el progreso del usuario, mantiene la coherencia del estado del juego y permite continuar o recuperarse sin corrupción de datos.

11. Risks and Technical Debts

Risk / Technical Debt Impact Mitigation / Notes

Complejidad de la integración Frontend ↔ Backend

La comunicación JSON/YEN entre TypeScript/React y Rust puede generar errores de formato o inconsistencias.

Definir y validar estrictamente el formato YEN, comprobar e integrar todos los endpoints. Tests E2E mockean la API para aislar el frontend.

Evolución de las estrategias de juego

Añadir nuevas estrategias puede romper el motor de juego si no se diseña modularmente.

El registro de bots (ybot_registry) centraliza la selección por ID. Añadir un nuevo bot requiere solo implementar el trait correspondiente y registrarlo.

Gestión de concurrencia en partidas multijugador

Usuarios jugando simultáneamente pueden generar conflictos de estado e incoherencias.

El ws-server gestiona la concurrencia mediante los mapas pendingRooms, activeRooms y wsToRoom. El estado de cada sala es independiente.

Estado no persistido en ws-server

Si el ws-server se reinicia, todas las partidas en curso se pierden.

Riesgo asumido en la versión actual. Mitigación futura: persistir estado de sala en Redis o MySQL.

Dependencia de bots externos

Bots externos pueden enviar datos inválidos o malformateados.

La API Rust valida estrictamente el formato YEN y las coordenadas antes de aplicar cualquier movimiento.

Seguridad del JWT_SECRET

Si JWT_SECRET se compromete, cualquiera puede firmar tokens válidos.

El secreto se inyecta como variable de entorno y no aparece en el código fuente. En producción debe rotarse periódicamente.

Expiración de tokens JWT y UX

Los tokens expiran en 1h (configurable). Si el usuario lleva mucho tiempo en una partida, la petición de guardado puede fallar con 401.

Deuda técnica: implementar refresh token o aumentar la expiración para sesiones largas. Actualmente el usuario debe volver a hacer login.

Deuda técnica en el frontend

Código TypeScript con React puede no ser modular o adquierir acoplamiento que dificulte el mantenimiento.

Revisiones de código, pruebas unitarias (Vitest) y E2E (Playwright + Cucumber) periódicas. Cobertura reportada en SonarCloud.

Escalabilidad del backend

El API y la lógica deben soportar múltiples partidas simultáneas.

Planificar escalabilidad desde el inicio; monitorización con Prometheus + Grafana para detectar cuellos de botella.

Ausencia de métricas en servicios distintos de users

Actualmente solo el Users Service expone métricas a Prometheus. Gamey, auth-service y ws-server no tienen instrumentación.

Deuda técnica: añadir endpoints de métricas en los demás servicios para tener visibilidad completa del sistema.

Compatibilidad con variantes futuras del juego

Añadir variantes como Master Y o Holey Y puede requerir cambios en la lógica.

El motor Rust es modular. La notación YEN es extensible. Riesgo bajo a corto plazo.

12. Glossary

Term Definition

Yovi

Nombre del proyecto, plataforma web para jugar al juego Y y permitir la interacción de usuarios y bots.

Juego Y (Hex)

Juego de tablero abstracto en el que dos jugadores intentan conectar lados opuestos del tablero siguiendo reglas específicas. El tablero tiene forma de pirámide triangular.

Notación YEN

Formato JSON utilizado para representar el estado de una partida: tamaño, turno, jugadores y disposición del tablero como string de filas separadas por /.

Bot

Programa que juega automáticamente. Disponibles: random_bot (aleatorio), greedy_bot (codicioso), minimax_bot (minimax con profundidad 2).

API (Application Programming Interface)

Conjunto de servicios web que permite la interacción de bots o módulos externos con la aplicación.

JWT (JSON Web Token)

Estándar de autenticación stateless. El auth-service firma un token con JWT_SECRET que el cliente incluye en cada petición. El gateway lo verifica localmente sin consultar la BD.

Auth Service

Microservicio Node.js responsable de la autenticación. Expone POST /auth/login (emite JWT) y POST /auth/register (proxy a users-service). No accede a MySQL directamente.

Bearer Token

Esquema de autenticación HTTP. El cliente envía Authorization: Bearer <token> en la cabecera de cada petición protegida.

JwtFilter

Filtro Servlet del Gateway (Spring Boot) que intercepta todas las peticiones, extrae y valida el JWT, y añade X-User-Id al request. Las rutas públicas (/auth/*, /status) se saltan el filtro.

Gateway

Servicio Spring Boot que actúa como punto de entrada HTTP único. Valida JWT y enruta peticiones REST al servicio correcto (users o gamey).

Nginx

Servidor web y proxy inverso. En producción sirve la SPA React y redirige /auth/* al auth-service, HTTP al gateway y WebSocket al ws-server, todo por el puerto 80/443.

WebSocket

Protocolo de comunicación bidireccional y persistente sobre HTTP. Usado para actualizar el tablero en tiempo real en el modo multijugador y para el chat de sala.

WS-Server

Microservicio Node.js que gestiona salas WebSocket: empareja jugadores por código, asigna colores aleatoriamente y retransmite movimientos y chat.

Código de sala

Identificador de 6 caracteres alfanuméricos generado al crear una sala multijugador. El segundo jugador lo introduce para unirse a la partida.

playerIndex

Índice asignado a cada jugador en una sala (0 = Azul, empieza primero; 1 = Rojo). Se asigna aleatoriamente en multijugador; el jugador elige en modo vs bot.

Frontend (WebApp)

Interfaz web implementada en TypeScript con React y Material UI. Incluye selector de modo de juego, tablero SVG, lobby multijugador con chat, historial de partidas, leaderboard y perfil de usuario.

HexBoard

Componente SVG del tablero hexagonal. Usa hexágonos flat-top en forma de pirámide centrada. Su función de geometría (buildHexGeometry) es compartida con GameHistory.

Flat-top hexagon

Orientación hexagonal con lados planos arriba y abajo, vértices a izquierda y derecha. Es la orientación usada en el tablero de Yovi.

Gamey Engine

Motor de juego implementado en Rust + Axum. Valida movimientos (/v1/game/play), calcula jugadas de bot (/v1/ybot/choose/{botId}) y detecta victorias.

GET /play

Endpoint de competición en Users Service. Recibe el estado actual del tablero en formato YEN como query param (position) y el identificador del bot (bot_id, por defecto random_bot). Devuelve las coordenadas { x, y, z } que elegiría el bot. Internamente llama a POST /v1/ybot/choose/{bot_id} en Gamey Engine.

Users Service

Microservicio Node.js + Express que gestiona usuarios (registro, login), partidas (guardar, listar), estadísticas de usuario, leaderboard y el endpoint de competición GET /play.

Leaderboard

Ranking global de jugadores ordenado por tasa de victorias (winRate). Paginado mediante parámetros limit y offset. Accesible via GET /api/leaderboard.

Estadísticas de usuario (stats)

Conjunto de métricas por jugador: total de partidas, victorias, derrotas, tasa de victorias, racha actual, top día, bots derrotados y fecha de registro. Accesibles via GET /api/users/:userId/stats.

Persistencia (MySQL)

Almacenamiento de usuarios, partidas y jugadores. Tres tablas principales: users, games, game_players.

bcrypt

Algoritmo de hashing de contraseñas usado en Users Service (10 salt rounds). Las contraseñas nunca se almacenan en texto plano.

Docker Compose

Herramienta de orquestación de contenedores. Levanta todos los microservicios (nginx, auth-service, gateway, users, gamey, ws-server, mysql, prometheus, grafana) con un solo comando.

Prometheus

Sistema de monitorización y alertas. Hace scraping de métricas del Users Service y las almacena como series temporales.

Grafana

Plataforma de visualización de métricas. Se conecta a Prometheus y muestra dashboards preconfigurados del estado del sistema.

Partida

Instancia de un juego Y entre dos jugadores (humanos o bots), con tablero y estado guardado en MySQL al finalizar.

Historial

Vista de la webapp que muestra todas las partidas guardadas con miniatura del tablero, jugadores, ganador y fecha.

CI/CD

Integración y despliegue continuos implementados con GitHub Actions. En cada push se ejecutan tests, se construyen las imágenes Docker y se publican en GHCR (GitHub Container Registry).

SonarCloud

Plataforma de análisis estático de código. Mide cobertura de tests, detecta code smells y vulnerabilidades. El estado del quality gate se muestra como badge en el README.