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

Describes the relevant requirements and the driving forces that software architects and development team must consider. These include

  • underlying business goals,

  • essential features,

  • essential functional requirements,

  • quality goals for the architecture and

  • relevant stakeholders and their expectations

1.1. Requirements Overview

The YOVI_EN1B project is a multi-service platform for playing the abstract board game Game Y (also known as the Y connection game), developed as part of the Software Architecture course at the University of Oviedo. It serves as a practical benchmark for the construction of scalable, maintainable, and well-tested distributed systems, with a specific focus on turn-based game logic and polyglot service integration.

Game Y is played on a triangular board composed of hexagonal cells. Each cell is uniquely identified by barycentric coordinates (x, y, z) where x + y + z = board_size − 1. Two players — Blue (B) and Red ® — take turns claiming cells. A player wins when their occupied cells form a single connected chain that touches all three sides of the triangle simultaneously. The board size is configurable (e.g. 5×5, 7×7, 11×11), and optional game variants such as Explosions (bomb cells) and Double Turn (coming soon) are available for boards of size 7 and above.

The following context diagram illustrates the primary boundaries of the system and its interactions with external actors and infrastructure:

context diagram

The functional scope encompasses several critical architectural domains:

  • User Identity Management: A centralized service built with Node.js and Express, responsible for registration, authentication (JWT + bcrypt), session management, and persistent user statistics. It is the single gateway to MongoDB and owns all persistence concerns.

  • Hexagonal Game Logic Engine: A high-performance application core implemented in Rust, governing spatial operations on a triangular board of hexagonal cells. This includes move validation, win condition detection via a Union-Find algorithm over barycentric coordinates, bot AI strategy dispatch, and YEN (Y Exchange Notation) game state serialization.

  • AI Opponent System: A pluggable bot registry offering multiple difficulty levels: a Random bot for basic play, a Defensive bot that mirrors opponent threats, a Hard bot powered by Monte Carlo Tree Search (MCTS), and an optional GenerativeAI bot driven by the Google Gemini API. All bots implement a common YBot trait, making them interchangeable at runtime.

  • Game Variants: Optional rule modifiers that extend standard gameplay. The Explosions variant places a hidden bomb cell on the board; landing on it clears all neighbouring pieces via chain-detonation. The Double Turn variant lets each player place two pieces per round. Both require a minimum board size of 7.

  • State Persistence and Resilience: Integration with MongoDB to record all game sessions, move histories, and player statistics. The persistence model ensures that completed games can be replayed move-by-move and that user records survive service restarts.

  • Third-party Bot API: The Gamey engine exposes a documented public HTTP API (POST /compute, POST /play) that allows external automated agents to participate in the game, fulfilling the course constraint for open extensibility.

1.2. Quality Goals

The architecture of YOVI_EN1B is governed by a technical quality tree rooted in the ISO 25010 standard. The following image shows the ISO 25010 quality model used as a reference:

ISO 25010 quality model

These high-priority requirements ensure the technical integrity and operational success of the platform:

Priority Goal Architectural Attribute Operational Scenario

1

Maintainability

Modularity

The system is decomposed into three independent services (webapp, users, gamey) communicating only via HTTP/JSON. Replacing or upgrading one service — for example, swapping the Rust engine for a new version — requires no changes to the other services. SonarCloud quality gate enforces ≥80% coverage on new code and 0 critical issues.

2

Reliability

Fault Tolerance

All game sessions, moves, and user statistics are persisted in MongoDB by the users service. In the event of a service restart, the full game history is recoverable from the database; active sessions are not lost. Full environment is reproducible with a single docker compose up on any machine with Docker.

3

Performance

Efficiency

The Gamey Rust engine executes move validation and win detection in under two milliseconds, even on large boards. Bot move response (POST /play) returns in under 500ms for board size ≤ 7 under normal load.

4

Security

Data Integrity

All protected API endpoints require a valid JWT Bearer token issued at login (24-hour expiry). Passwords are never stored in plain text — they are hashed with bcrypt (salt rounds 10). All protected endpoints return 401 within 50ms when no token is provided.

5

Usability

Accessibility

The React frontend renders the triangular hexagonal board with clear visual feedback of the current game state. WCAG 2.0 AAA compliance verified via Web Accessibility Checker with 0 known issues. A replay feature lets players review past games move by move.

1.3. Stakeholders

The following individuals have a significant interest in the architectural success and technical implementation of the YOVI_EN1B system:

Role or Name Contact Expectations

Jose Emilio Labra Gayo

University of Oviedo, Course Professor

Evaluates architectural decisions, documentation quality, and adherence to arc42 and ADR standards.

Pablo Gonzalez

University of Oviedo, Course Instructor

Reviews technical implementation, testing coverage, and CI/CD pipeline.

Diego Martín Fernández

University of Oviedo, Course Instructor

Assesses system deployment, scalability decisions, and Docker containerization.

Celia Melendi Lavandera

University of Oviedo, Course Instructor

Reviews quality requirements, observability, and monitoring implementation.

bilalyazicioglu

University of Oviedo, Developer

Implements and maintains the Gamey Rust engine: game logic, bot AI, YEN notation, and the public bot API.

UO300896

University of Oviedo, Developer

Designs and maintains the MongoDB schema, data persistence strategy, and database initialization.

Th0be

University of Oviedo, Developer

Builds and maintains the React/TypeScript frontend: UI components, board rendering, and end-to-end user flows.

nacho50900

University of Oviedo, Developer

Develops and maintains the Node.js/Express users service: authentication, game orchestration, and REST API.

2. Architecture Constraints

Contents

Any requirement that constraints software architects in their freedom of design and implementation decisions or decision about the development process. These constraints sometimes go beyond individual systems and are valid for whole organizations and companies.

Motivation

Architects should know exactly where they are free in their design decisions and where they must adhere to constraints. Constraints must always be dealt with; they may be negotiable, though.

Form

Simple tables of constraints with explanations. If needed you can subdivide them into technical constraints, organizational and political constraints and conventions.

2.1. Technical Constraints

Constraint ID Constraint Impact Rationale

TC-01

Mandatory Tech Stack

Web application must use TypeScript; core game logic (move validation/generation) must be in Rust.

Requirement defined by the ASW course to practice polyglot development and language interoperability.

TC-02

Communication Protocol

Subsystems must communicate via JSON messages.

Standardizes data exchange between the TypeScript frontend and the Rust backend service.

TC-03

Game Representation (YEN)

All game states and moves must be represented using the YEN notation.

Ensures consistency in how the Game Y board is serialized and stored across the system.

TC-04

Architecture Decomposition

System must be split into at least two subsystems: Web Application and Rust-based web service.

Encourages modularity and demonstrates the ability to integrate different architectural components.

TC-05

External API

The system must expose a documented API for third-party bots.

Facilitates extensibility and allows the game to be played by automated agents.

TC-06

Public Deployment

The application must be accessible via a public URL.

Demonstrates the ability to manage cloud deployment and production environments.

2.2. Organizational Constraints

Constraint ID Constraint Impact Rationale

OC-01

Version Control

All source code and documentation must be hosted in the assigned GitHub repository (Arquisoft/yovi_en1b).

Enables collaborative development and allows instructors to track progress and contributions.

OC-02

Automated CI/CD

Deployment and testing must be automated using CI/CD pipelines (e.g., GitHub Actions).

Ensures software quality and rapid feedback loops throughout the development lifecycle.

OC-03

Documentation Standards

Use of the arc42 template and ADRs (Architectural Decision Records) is mandatory.

Provides a standardized structure for architectural documentation and history of key decisions.

2.3. Conventions

Constraint ID Constraint Impact Rationale

C-01

Testing Requirements

Mandatory inclusion of Unit, Integration, E2E, and Load tests.

Critical for verifying system reliability and performance under stress.

C-02

AI Strategy Options

The AI must offer at least two different strategies or difficulty levels.

Enhances user experience and demonstrates implementation of different algorithmic approaches.

C-03

Language

All documentation and code comments should be in English.

Standard practice in software engineering.

3. Context and Scope

This section delimits the GameY system from all its external communication partners. It defines the boundaries and the interfaces between our system and its environment, both from a business/domain perspective and a technical perspective.

3.1. Business Context

The business context focuses on the external entities (actors) that interact with the GameY system from a functional point of view, ignoring technical details.

business context

Explanation of external domain interfaces:

Communication Partner Inputs (to the System) Outputs (from the System)

User / Player

Registration credentials, login credentials, game moves, game configuration (board size, strategy).

Board rendering, game state updates, move results, statistics, match history, leaderboard.

3.2. Technical Context

This section describes the technical interfaces, channels, and transmission media linking the GameY system to its environment. Based on the docker-compose configuration, the project is divided into a frontend application (webapp), a backend for user handling (users), and a game engine (gamey).

technical context

Mapping Input/Output to Channels:

Communication Partner I/O Channel / Protocol

User (Client Device)

UI interactions, form submissions, game moves, board configuration.

HTTP for fetching the SPA from the webapp service (Port 80). Once loaded, the browser communicates via HTTP REST with the users service API (Port 3000) for all operations — authentication, game management, statistics, and history. The users service internally delegates game logic to the gamey engine (Port 4000); the frontend does not call gamey directly.

4. Solution Strategy

The GameY project follows a layered architecture divided into UI, game logic, and backend user handling. Each layer is independently deployable and communicates exclusively via HTTP/JSON.

4.1. Technology decisions

  • The UI is built with React, Vite, and TypeScript; React was chosen due to team familiarity, Vite and TypeScript are course requirements.

  • The game logic is implemented in Rust (course requirement), providing high performance for move validation, win detection, and bot AI.

  • User data and game history are persisted in MongoDB, whose document-oriented model fits naturally with the nested, variable-length structure of game states and move histories. It also integrates well with the Node.js ecosystem via Mongoose. See ADR for full rationale.

  • GitHub is used for version control and CI/CD (course requirement).

  • Docker Compose is used for local and production deployment, orchestrating all four containers with a single command (course requirement).

  • Microsoft Azure (Azure VM) hosts the production environment, chosen for its "Azure for Students" credits and native GitHub Actions integration. See ADR for full rationale.

4.2. Quality goals

  • Maintainability is achieved by decomposing the system into three independent services (webapp, users, gamey) that communicate only via HTTP/JSON contracts, allowing each to be updated independently.

  • Reliability is ensured by the MongoDB persistence layer — all game sessions and moves are persisted immediately, so the system can recover from service restarts without data loss.

  • Performance is handled by the Rust engine, which runs move validation and win detection in under 2ms using a Union-Find algorithm with path compression.

  • Security is enforced through bcrypt password hashing, JWT authentication with 24-hour expiry, and input validation on all endpoints.

  • Usability is achieved through a clear React UI with immediate visual feedback, a replay feature for past games, and accessible design.

5. Building Block View

This section describes the decomposition of the GameY system into its principal building blocks, showing their internal structure and relationships. The system is detailed across two levels: Level 1 shows the overall system components, and Level 2 zooms into the internal structure of each specific component.

5.1. Whitebox Overall System (Level 1)

Here we describe the decomposition of the overall GameY system.

Overview Diagram

level 1 overall

Motivation

The system is decomposed into three main building blocks to separate concerns: * The user interface (frontend) is isolated to provide a responsive SPA (Single Page Application). * The user handling is separated into its own microservice to independently manage registrations and user logic. * The game logic is encapsulated in a high-performance Rust engine, ensuring fast execution for bot AI and game state validation.

Contained Building Blocks

Name Responsibility

webapp

Frontend single-page application. Renders the game board, handles user registration and login, displays statistics, match history, leaderboard, and replay. Communicates exclusively with the users service via HTTP REST.

users

Backend REST API service. Handles user registration, authentication (JWT + bcrypt), game creation and management, move orchestration, statistics tracking, leaderboard, and game history. Acts as the single gateway to MongoDB and delegates game logic to the gamey engine.

gamey

Core game engine and bot service written in Rust. Responsible for move computation, win condition detection (Union-Find), bot AI strategy dispatch (random, defensive, MCTS, Gemini), and YEN notation serialization. Stateless; no database connection.

5.2. Level 2

This level zooms into the internal structure of the three main building blocks defined in Level 1.

5.2.1. Whitebox: webapp

Overview Diagram

level 2 webapp

Contained Building Blocks

Name Responsibility

App.tsx

Root React component; sets up routing and global state.

Game board components

Renders the triangular hexagonal board using YEN state, handles cell selection and move submission.

Auth components

Registration and login forms; stores JWT token for authenticated requests.

History and replay components

Displays past games and drives the step-by-step move replay using GET /games/:id/moves.

Leaderboard component

Fetches and displays the ranked player list from GET /leaderboard.

5.2.2. Whitebox: users

Overview Diagram

level 2 users

Contained Building Blocks

Name Responsibility

users-service.js

Main Express server entry point. Mounts all route modules and connects to MongoDB via Mongoose.

authRoutes

Handles POST /createuser and POST /login; bcrypt password hashing and JWT signing.

userRoutes

Handles GET /users/:id and GET /users/:id/history; returns profile and game history.

gameRoutes

Handles game lifecycle: POST /games, GET /games/:id, POST /games/:id/move, GET /games/:id/play, PUT /games/:id/finish, POST /games/:id/undo, GET /games/:id/moves, GET /games/options.

playRoute

Public POST /play endpoint; proxies directly to Gamey without requiring authentication or an internal game ID.

leaderBoardRoute

GET /leaderboard; returns overall and per-strategy ranked player lists.

MongoUserRepository

Data access layer implementing the repository pattern. Encapsulates all MongoDB queries for users, games, and statistics.

5.2.3. Whitebox: gamey

Overview Diagram

level 2 gamey

Contained Building Blocks

Name Responsibility

main.rs / lib.rs

Entry point and library exports for the Rust application. It initializes the engine (e.g., HTTP server mode on Port 4000).

web

Manages the web interface components and server routing.

core

Contains the core game logic, including actions, coordinates, game state, and player management.

bot

Implements the bot registry and automated player logic.

notation

Provides parsers and support for game notation formats (YEN, YGN).

6. Runtime View

Here are the main runtime scenarios that demonstrate how YOVI’s components interact during gameplay.

The system consists of three components:

  • Frontend (FE) — React + Vite, runs on port 80

  • Users Service (USERS) — Node.js + Express + MongoDB, runs on port 3000

  • Gamey Logic (GAMEY) — Rust game engine, runs on port 4000


6.1. Scenario 1: User Registration and Login

auth sequence

Notable aspects:

  • Passwords are never stored in plain text — bcrypt with salt rounds 10

  • JWT token expires after 24 hours and must be sent as Authorization: Bearer <token> on all protected requests

  • Username uniqueness is enforced at DB level (unique index)


6.2. Scenario 2: Player vs Player Game

pvp sequence

Notable aspects:

  • Starting turn (B or R) is randomly assigned using crypto.randomInt — cryptographically secure

  • Player field in each move is inferred from current_turn — frontend does not send it

  • yen_state per move is computed by Gamey’s /compute endpoint, not by the frontend or backend

  • Gamey returns a winner field after every /compute call — see Scenario 4 for win detection details

  • DRAW result (user quit) does not update statistics


6.3. Scenario 3: Player vs Bot Game

pve sequence

Notable aspects:

  • The backend acts as a proxy between the frontend and Gamey — the frontend never calls Gamey directly

  • POST /compute — Gamey receives { yen_state_prev, coordinates } and returns { yen_state, winner }

  • POST /play — Gamey receives { yen_state, strategy, difficulty_level, board_size } and returns { coordinates, yen_state, winner }

  • Both endpoints return a winner field — see Scenario 4 for win detection details

  • Bot move is saved automatically in the DB by the Users Service — frontend does not need to call POST /games/:id/move for the bot

  • If it is the first move, yen_state_prev / yen_state is sent as null and Gamey generates the initial board state from board_size


6.4. Scenario 4: Win Detection

win detection sequence

Notable aspects:

  • Win detection is fully handled by Gamey — the Users Service only reacts to the winner field

  • The backend calls autoFinishIfWinner after every move (both player and bot) — if winner is not null, the game is finished and statistics updated automatically

  • winner: "B" → human player wins → result is WIN; winner: "R" → bot wins → result is LOSS

  • Duration is calculated automatically from game.created_at when auto-finishing


6.5. Scenario 5: Game Replay

replay sequence

Notable aspects:

  • Each move stores its resulting yen_state, so the frontend can reconstruct the board at any point in time

  • History endpoint excludes the moves array for a lighter response — full moves are fetched separately via GET /games/:id/moves

7. Deployment View

Content

The deployment view describes:

  1. technical infrastructure used to execute your system, with infrastructure elements like geographical locations, environments, computers, processors, channels and net topologies as well as other infrastructure elements and

  2. mapping of (software) building blocks to that infrastructure elements.

Often systems are executed in different environments, e.g. development environment, test environment, production environment. In such cases you should document all relevant environments.

Especially document a deployment view if your software is executed as distributed system with more than one computer, processor, server or container or when you design and construct your own hardware processors and chips.

From a software perspective it is sufficient to capture only those elements of an infrastructure that are needed to show a deployment of your building blocks. Hardware architects can go beyond that and describe an infrastructure to any level of detail they need to capture.

Motivation

Software does not run without hardware. This underlying infrastructure can and will influence a system and/or some cross-cutting concepts. Therefore, there is a need to know the infrastructure.

Form

The deployment view uses UML deployment diagrams to express the infrastructure. Nested diagrams show the internal structure of containers when needed.

Further Information

See Deployment View in the arc42 documentation.

7.1. Infrastructure Level 1

Describe (usually in a combination of diagrams, tables, and text):

  • distribution of a system to multiple locations, environments, computers, processors, .., as well as physical connections between them

  • important justifications or motivations for this deployment structure

  • quality and/or performance features of this infrastructure

  • mapping of software artifacts to elements of this infrastructure

For multiple environments or alternative deployments please copy and adapt this section of arc42 for all relevant environments.

deployment overview
Motivation

Game Y uses Docker Compose to orchestrate four independent containers: the webapp (React frontend), the users service (Node.js/Express user management API), the gamey service (Rust game engine), and a mongodb container for persistent storage. This separation ensures that each service can be developed, built, and scaled independently. MongoDB is the chosen database because its document-oriented model fits naturally with the variable structure of game states and move histories, and it integrates well with the Node.js ecosystem.

Only the users service connects to MongoDB. This is a deliberate architectural decision: gamey manages active game state in memory during a match (which is sufficient for its role as a pure game engine), and once a game ends the result is sent to the users service, which is responsible for all persistence — player accounts, statistics, and game history. This avoids two services competing to write to the same database and keeps data ownership clear.

Quality and/or Performance Features
  • Isolation: Each service runs in its own Docker container, preventing dependency conflicts and simplifying upgrades.

  • Portability: docker-compose up --build reproduces the full environment on any machine with Docker installed.

  • Independent scaling: The gamey and users services can be scaled individually based on load without affecting the frontend.

  • Clear interface boundaries: Services communicate only via HTTP REST, making the contracts explicit and testable.

  • Development efficiency: Docker Compose orchestrates the full local setup with a single command; no manual service wiring is required.

Mapping of Building Blocks to Infrastructure
Building Block Container Technology Exposed Port Connects to

User Interface

Webapp Container

React + TypeScript + Vite

80

Users (3000)

User Management & Persistence

Users Container

Node.js + Express

3000

MongoDB (27017), Gamey (4000)

Game Logic & Bot

Gamey Container

Rust (Cargo)

4000

Persistent Storage

MongoDB Container

MongoDB 7

27017 (internal)

7.1.1. Container Descriptions

Webapp Container — Serves the React single-page application to the browser. It is the only container directly accessible from outside the Docker network. It communicates exclusively with the users service for all operations: authentication, game creation, move submission, statistics, history, and leaderboard. It never calls the gamey engine directly.

Users Container — Manages player accounts, authentication, statistics, and game history. It is the only service that connects to MongoDB, acting as the single gateway for all persistent data. When a game finishes, the result is reported here so it can be stored and reflected in player stats.

Gamey Container — A pure game engine written in Rust. It handles game creation, move validation, win condition checking, and bot moves entirely in memory. It has no database connection; active game state lives in memory for the duration of a match. Once the game ends, the result is communicated back to the frontend, which then reports it to the users service.

MongoDB Container — Provides persistent NoSQL storage exclusively for the users service. It runs as a Docker container within the same Compose network, so it is not directly accessible from outside.

7.2. Infrastructure Level 2

Here you can include the internal structure of (some) infrastructure elements from level 1.

Please copy the structure from level 1 for each selected element.

7.2.1. Gamey Container

gamey detail

The Gamey container runs the Rust application that implements the full game logic for Game Y. Its internal components are:

  • Web Interface: Exposes HTTP endpoints on port 4000 that the React frontend calls to create games, submit moves, and query game state.

  • Game Engine (core): Contains the core data model — coordinates, board state, player management, and action dispatch. This is the authoritative source for game rules. Win condition checking is performed internally within the engine after every move, using a graph traversal (DFS/BFS) over the hexagonal grid to detect whether a player’s connected chain touches all three sides of the triangular board (see section 6 for the runtime scenario).

  • Bot Registry: Maintains a set of registered bot strategies. When a player chooses to play against the computer, the bot registry selects the appropriate bot and returns its move to the game engine.

  • Notation Parser: Supports YEN (Yovi Extended Notation) and YGN (Yovi Game Notation) for encoding and decoding move sequences. This enables game export, replay, and interoperability.

7.2.2. Users Container

users detail

The Users container is a Node.js/Express service that acts as the main backend for the platform. It handles user registration, authentication (JWT + bcrypt), game lifecycle management, statistics tracking, leaderboard, and the public bot API. It is the only service that connects to MongoDB (via Mongoose) and the only service that calls the Gamey engine internally — the frontend never reaches Gamey directly. Routes are split into independent modules: authRoutes, userRoutes, gameRoutes, playRoute, and leaderBoardRoute, all backed by a MongoUserRepository implementing the repository pattern.

7.2.3. Webapp Container

The Webapp container serves the React single-page application compiled by Vite. It is served via nginx on port 80, configured with try_files $uri /index.html to support client-side routing without 404s on page reload. All interactions are handled client-side, with REST calls made exclusively to the users service (port 3000). There is no server-side rendering and no direct connection to the gamey engine.

7.2.4. MongoDB Container

mongodb detail

The MongoDB container is exclusively used by the users service. It holds two collections:

  • players: User accounts, hashed credentials, and per-player statistics (wins, losses, games played).

  • game_history: Archived completed games including the full sequence of moves, the result, and the participants. This allows the frontend to display match history and enables future replay functionality.

gamey does not connect to MongoDB. Active game state is kept in memory within the Rust process for the duration of a match, which is sufficient for its role as a game engine. When a game concludes, the frontend reports the result to the users service, which writes it to game_history.

The MongoDB port (27017) is only exposed internally within the Docker Compose network and is not reachable from outside the host machine.

The docker-compose entry for this container is:

mongodb:
  image: mongo:7
  volumes:
    - mongo_data:/data/db

The named volume mongo_data ensures that data persists across container restarts. Note that the port is intentionally not published to the host — only the users container needs to reach it, and it can do so through the internal Docker network.

8. Cross-cutting Concepts

Content

This section describes overall, principal regulations and solution ideas that are relevant in multiple parts (= cross-cutting) of your system. Such concepts are often related to multiple building blocks. They can include many different topics, such as

  • models, especially domain models

  • architecture or design patterns

  • rules for using specific technology

  • principal, often technical decisions of an overarching (= cross-cutting) nature

  • implementation rules

Possible topics for crosscutting concepts

See Concepts in the arc42 documentation.

8.1. Domain Model: Barycentric Coordinate System

The Game Y board is a triangle subdivided into hexagonal cells. Every cell is uniquely addressed by a triple of non-negative integers (x, y, z) — barycentric coordinates — that satisfy the invariant:

x + y + z = board_size − 1

Each coordinate component encodes the cell’s distance from one of the three sides of the triangle:

  • x = 0 → cell touches side A (bottom edge)

  • y = 0 → cell touches side B (left edge)

  • z = 0 → cell touches side C (right edge)

Corner cells touch exactly two sides (e.g. (0, 0, n) touches both A and B); interior cells touch none. A board of size N contains exactly N(N+1)/2 cells in total.

Adjacency is defined as any coordinate reachable by incrementing one component by 1 and decrementing another by 1, keeping the sum constant. Interior cells have six neighbours; edge cells have four; corner cells have two. This model is implemented in gamey/src/core/coord.rs and is the authoritative coordinate representation used throughout the entire system.

domain model

8.2. Game State Serialization: YEN (Y Exchange Notation)

All game states exchanged between the Users Service and the Gamey engine are encoded as YEN (Y Exchange Notation) — a compact, JSON-serializable format inspired by FEN (Forsyth-Edwards Notation) used in chess. YEN is the single wire format for game state across the entire system (constraint TC-03).

A YEN document contains four mandatory fields and two optional ones:

Field Type Description

size

integer

Side length of the triangular board (e.g. 5 for a 5×5 board with 15 cells).

turn

integer (0 or 1)

Index of the player whose turn it is next.

players

array of chars

Symbol for each player, e.g. ["B", "R"] for Blue and Red.

layout

string

Compact board encoding: rows separated by /, each cell represented by its player symbol or . for an empty cell. Rows are ordered from the top vertex downward.

variants

array of strings (optional)

Active game variant names, e.g. ["Explosions"].

e

string (optional)

Comma-separated flat cell indices of bomb positions, e.g. "3,14".

Example YEN for a board of size 3 where Blue has claimed the top vertex:

{
  "size": 3,
  "turn": 1,
  "players": ["B", "R"],
  "layout": "B/../.."
}

The layout string encodes rows from top to bottom: row 0 has 1 cell, row 1 has 2, row r has r+1. An empty board of size 3 is "./../.." (or equivalently "…​"`with the `/ separators omitted for a flat representation; the Gamey parser handles both). YEN is generated and consumed exclusively by gamey/src/notation/yen.rs.

8.3. Win Detection: Union-Find with Side-Touching Flags

Win detection is a cross-cutting concern executed by the Gamey engine after every move and returned to the Users Service as a winner field. A player wins when their occupied cells form a single connected component that simultaneously touches all three sides of the triangular board.

The Gamey engine implements this with an incremental Union-Find (Disjoint Set Union) data structure augmented with three boolean flags per set root: touches_side_a, touches_side_b, and touches_side_c.

The algorithm works as follows:

  1. When a cell is placed, a new singleton set is created for it with the side-touching flags derived from its coordinates.

  2. The cell’s neighbors already owned by the same player are merged into the new set using union() with path compression.

  3. During union(), the merged root inherits the bitwise OR of both roots' side-touching flags.

  4. If the merged root has all three flags set (touches_side_a ∧ touches_side_b ∧ touches_side_c), place_piece() returns true — the placing player wins immediately.

This approach runs in near-constant time per move (amortized O(α(n)) with path compression), replacing the need for a full DFS/BFS traversal after each move. The implementation lives in gamey/src/core/board.rs.

Win condition check per move:
  1. create set S for new cell C with flags {a=touches_A?, b=touches_B?, c=touches_C?}
  2. for each neighbor N of C owned by same player:
       root_S ← find(S),  root_N ← find(N)
       merge(root_S, root_N): root.flags |= merged.flags
  3. if root.touches_a AND root.touches_b AND root.touches_c → winner!

After a bomb detonation (Explosions variant), the union-find structure is fully rebuilt from the surviving pieces to prevent stale side-touching flags from leaked orphaned sets causing phantom wins.

8.4. Security Concepts

Security concerns are handled uniformly across all endpoints that require an authenticated user. The following mechanisms are in place:

Password Storage: Passwords are never stored in plain text. When a user registers, the password is hashed using bcrypt with 10 salt rounds before it is written to MongoDB. On login, bcrypt.compare() is used to verify the supplied password against the stored hash. The plain-text password is never logged or retained.

Session Authentication (JWT): Upon successful login, the Users Service signs a JSON Web Token (JWT) with a 24-hour expiry. The token is returned to the client and must be included as a Authorization: Bearer <token> header on all protected API calls. The server validates the signature and expiry on every request before processing it.

Username Uniqueness: A unique index on the username field in MongoDB enforces that no two accounts can share the same username, even under concurrent registration requests.

Input Validation: All incoming JSON payloads are validated for required fields and correct types before any business logic is executed. Invalid requests are rejected with a 400 response before they reach the database or the game engine.

The Gamey engine itself does not perform authentication — it is an internal service reachable only within the Docker Compose network, not directly exposed to the public internet.

8.5. Inter-service Communication

All three services communicate exclusively via synchronous HTTP REST with JSON payloads (constraint TC-02). No message broker, shared memory, or direct database access across services is used.

Direction Protocol Purpose

Browser → webapp

HTTPS/HTTP (port 80)

Serves the React SPA to the client.

webapp → users

HTTP REST (port 3000)

User registration, login, game creation, move submission, history retrieval.

webapp → gamey

HTTP REST (port 4000)

Not used directly by the frontend in production. Gamey is called internally by the users service.

users → gamey

HTTP REST (port 4000)

POST /compute to validate a move and get the new YEN state; POST /play to get a bot move.

External bots → gamey

HTTP REST (port 4000)

Third-party automated agents may call /compute and /play directly (constraint TC-05).

users → MongoDB

MongoDB Wire Protocol (port 27017)

All persistence operations — user accounts, game records, move histories.

The users service acts as an orchestrator: it receives a player move from the frontend, delegates the state computation to Gamey, persists the result in MongoDB, and returns the updated game to the frontend. The frontend never calls Gamey directly during a live game session.

8.6. Persistence Model

Persistent state is owned exclusively by the Users Service and stored in MongoDB. MongoDB’s document model was chosen because game states and move histories have a variable, nested structure that maps naturally to JSON documents (see deployment decision in Section 4).

The database contains two collections:

players collection: Each document represents a registered user and stores the username, the bcrypt-hashed password, and aggregated statistics (wins, losses, games played). A unique index on username ensures account integrity.

games collection: Each document represents a single game session. It stores the participants, the board size, the game type (PLAYER or BOT), the current status (IN_PROGRESS or FINISHED), the final result (WIN, LOSS, or DRAW), and an embedded array of moves. Each move document records the player who moved, the coordinates chosen, the resulting YEN state, and a sequential move number. This embedded design allows the full game to be retrieved and replayed in a single query.

The gamey engine intentionally has no database connection. Active game state lives in the Rust process memory for the duration of a match. When a game concludes, the result is propagated back through the users service, which writes it to MongoDB. This clear ownership boundary avoids write conflicts and keeps the data model simple.

8.7. Bot AI Strategy Pattern

The bot AI system in Gamey is structured around the Strategy pattern. All bots implement the YBot trait, which exposes a single method: given a board state (as a YEN document), return the chosen coordinates. This interface decouples the bot selection logic from the game engine core and makes it trivial to add new strategies without modifying existing code.

The following bot strategies are registered in the YBotRegistry at startup:

Strategy Difficulty Description

RandomBot

Easy

Selects a uniformly random empty cell. Used as a baseline and for testing.

DefensiveBot

Medium

Identifies and blocks the cell adjacent to the opponent’s most recent move, prioritising defence over offence.

HardBot

Hard

Implements Monte Carlo Tree Search (MCTS) — simulates many random playouts from the current position to estimate the value of each candidate move and selects the highest-value option.

GenerativeAIBot

Variable

Delegates move selection to the Google Gemini API via GEMINI_API_KEY. The model receives the current YEN state and responds with coordinates. Used for experimental AI integration.

The strategy and difficulty level are selected by the frontend when creating a bot game and passed to Gamey on each POST /play call. The registry looks up the matching YBot implementation and invokes it, returning the coordinates to the users service.

8.8. Game Variants

Game variants are optional rule modifiers that extend the standard Y game. They are represented as an enum (GameVariant) in the Rust engine and encoded in the variants array of the YEN state. Variant Explosions require a minimum board size of 7.

Explosions (Bomb Mode): At game initialization, one or more bomb cells are randomly placed on the board. Their positions are encoded in the YEN e field as a comma-separated list of flat cell indices. A bomb cell looks empty to players — they can choose it as a move. When a player lands on a bomb cell: (1) the player’s piece is placed normally, (2) the bomb is consumed, and (3) all occupied neighbour cells are cleared back to empty via a BFS chain-detonation (adjacent bombs also detonate). After detonation, the union-find structure is fully rebuilt from surviving pieces to maintain win-detection integrity. Chain detonation means a cluster of adjacent bombs all explode together when the first one is triggered.

8.9. Error Handling

Error handling follows a centralized, typed model in the Rust engine and a consistent HTTP status code convention in the Node.js service.

In Gamey, all possible failure modes are expressed as variants of a single GamyError enum (defined in gamey/src/gamey_error.rs). This enum is used throughout the engine and the HTTP handlers, and maps to appropriate HTTP status codes (e.g. 400 for invalid moves, 404 for unknown games, 500 for internal failures). This avoids stringly-typed errors and ensures all error paths are handled exhaustively.

In the Users Service, errors are caught at the controller layer and returned as JSON responses with a standard shape: { "error": "<message>" }. HTTP status codes follow REST conventions: 201 for resource creation, 200 for retrieval, 400 for validation failures, 401 for authentication errors, 404 for not-found resources, and 500 for unexpected failures.

The frontend displays error messages returned from the API directly to the user where relevant (e.g. "Username already taken" during registration, "Invalid move" during gameplay).

9. Architecture Decisions

All project-specific architecture decisions are collected in one place. You can find them all in the wiki here: link: https://github.com/Arquisoft/yovi_en1b/wiki/Architectural-decisions. This page is the central reference for decisions made in the project.

10. Quality Requirements

10.1. Quality Tree

The following mind map illustrates the primary quality goals for YOVI, breaking them down into concrete quality scenarios (S1 to S8) strictly based on the system’s actual architecture and testing capabilities.

quality tree

10.2. Quality Scenarios

The table below describes the specific scenarios derived from the quality tree, providing concrete and measurable situations to evaluate the architecture based on the defined goals and the actual test suites present in the repository.

Ref Quality Attribute Scenario Description Priority

S1

Maintainability (Modularity)

Updating the Rust engine or Node API can be done independently without affecting the WebApp, as long as the REST HTTP/JSON contracts remain unchanged.

High

S2

Reliability (Fault Tolerance)

If the Users service restarts unexpectedly, active players can resume their games seamlessly since all moves and states are immediately persisted to MongoDB.

High

S3

Performance (Efficiency)

The Gamey engine computes move validation and win conditions using Union-Find in under 2ms per request, ensuring rapid turn-based responses.

High

S4

Performance (Scalability)

During automated load testing (e.g., using Artillery), the system is capable of sustaining peak loads of up to 15 concurrent complete user lifecycles per second without dropping requests.

High

S5

Security (Data Integrity)

Unauthenticated requests to modify game state are blocked (401 Unauthorized), and database dumps cannot reveal passwords because they are salted and hashed via bcrypt (10 rounds).

High

S6

Usability (Accessibility)

Players are provided with immediate visual feedback on invalid moves in the React interface, and can use the history replay feature to review past matches step-by-step.

Medium

S7

Testability (E2E Automation)

Critical user journeys (register, login, play against bots) are continuously verified in the CI/CD pipeline using Playwright and Cucumber against a full containerized environment.

High

S8

Testability (State Isolation)

Automated test suites can clean up their own database footprint after execution using test-specific endpoints (like DELETE /deleteuser), ensuring tests are idempotent and repeatable.

High

11. Risks and Technical Debts

Contents

A list of identified technical risks or technical debts, ordered by priority

Motivation

"Risk management is project management for grown-ups" (Tim Lister, Atlantic Systems Guild.)

This should be your motto for systematic detection and evaluation of risks and technical debts in the architecture, which will be needed by management stakeholders (e.g. project managers, product owners) as part of the overall risk analysis and measurement planning.

Form

List of risks and/or technical debts, probably including suggested measures to minimize, mitigate or avoided risks or reduce technical debts.

Further Information

See Risks and Technical Debt in the arc42 documentation.

11.1. Technical Risks

Priority Risk Impact Mitigation

1 — Critical

Gamey service instability under load The Rust game engine has no persistent state and manages active game sessions in memory. Under concurrent load it has shown connection resets and crashes, requiring a full container restart to recover.

Total service unavailability for all active games. Users lose their current game session with no recovery path.

Add a health-check endpoint to Gamey and configure Docker Compose restart: always. Implement a timeout (5 s) and circuit breaker in the Users service fetch calls to Gamey so that a crash returns a clean 503 rather than hanging.

2 — High

Single point of failure: Users service and MongoDB on one VM The entire backend (Users service, MongoDB, Gamey) runs on a single Azure VM (IP 4.233.184.98). No redundancy or failover exists.

A VM outage, network issue, or resource exhaustion takes down the entire platform simultaneously.

Document the risk explicitly. For the scope of this course, accept it. For production, migrate to a managed service (e.g., MongoDB Atlas) and deploy behind a load balancer with at least two VM instances.

3 — High

JWT secret not rotated; hardcoded fallback in code The JWT signing secret is loaded from an environment variable but falls back to a hardcoded default if unset. A leaked or default secret allows token forgery for any user.

Full authentication bypass — an attacker could forge tokens and impersonate any user including administrators.

Enforce the environment variable as mandatory (throw on startup if unset). Rotate the secret and redeploy. Store it in a secrets manager rather than a .env file committed to the repo.

4 — Medium

No input sanitization on board coordinates The Users service forwards coordinates received from the frontend directly to Gamey without validating that they are integers within the valid triangular board range.

Malformed input could crash Gamey or cause undefined behaviour in the Rust engine, triggering a full service restart.

Add a validation middleware in the Users service that rejects moves where col > row or row >= board_size before forwarding to Gamey.

5 — Medium

Grafana excessive disk I/O degrading VM performance Grafana has been observed causing high disk I/O on the shared VM, slowing down all co-located services including the Users service and MongoDB.

Increased response latency across the entire platform, potential timeout cascades under normal load.

Tune Grafana’s retention policy and reduce scrape frequency in Prometheus. Consider moving monitoring to a separate lightweight instance or disabling Grafana between demos.

6 — Low

VITE_API_URL baked at build time The frontend URL for the Users service API is embedded at Docker image build time via a Vite build argument. Changing the VM IP or adding a domain requires a full image rebuild and redeploy.

Any infrastructure change (IP rotation, domain migration) breaks the frontend without a rebuild.

Use a runtime configuration file (e.g., /config.js served by nginx) instead of a build-time variable, so the URL can be changed without rebuilding the image.

11.2. Technical Debts

Priority Debt Remediation

1 — Resolved

Legacy bot strategy keys in MongoDB (Fixed) Early versions stored bot results under easy, medium, hard in statistics.vs_bot. Current code uses random, defensive, ai, and mcts. The schema migration has been applied and the database is now consistent.

Resolved. The migration script was run and the production database was wiped and restarted with docker compose down -v. No further action required.

2 — Medium

No rate limiting on public endpoints The public GET /play endpoint and the POST /createuser endpoint have no rate limiting. A single client can flood the service with requests, exhausting Gamey connections or enabling user enumeration.

Integrate express-rate-limit middleware on unauthenticated routes. Apply stricter limits to /createuser (e.g., 10 req/min per IP) and /play (e.g., 60 req/min per IP).

3 — Medium

Game moves array not excluded from history endpoint GET /games/:id returns the full game document including the moves array, which can be very large for long games and is not needed for listing purposes.

Already partially addressed: findGamesByPlayer uses .select('-moves'). Verify all listing endpoints consistently exclude moves and only expose them via the dedicated GET /games/:id/moves endpoint.

12. Glossary

Contents

The most important domain and technical terms that your stakeholders use when discussing the system.

You can also see the glossary as source for translations if you work in multi-language teams.

Motivation

You should clearly define your terms, so that all stakeholders

  • have an identical understanding of these terms

  • do not use synonyms and homonyms

Form

A table with columns <Term> and <Definition>.

Potentially more columns in case you need translations.

Further Information

See Glossary in the arc42 documentation.

Term Definition

ADR (Architecture Decision Record)

A short document that captures an important architectural decision, the context that motivated it, and its consequences. Used in this project to track key design choices such as the technology stack and communication protocols.

arc42

A template for documenting software and system architecture. Provides a standardised structure of 12 sections covering goals, constraints, building blocks, runtime behaviour, deployment, and quality requirements.

ASW (Arquitectura del Software)

Software Architecture course at the Universidad de Oviedo in which this project (YOVI_EN1B) was developed.

Authorization Header

An HTTP request header used to transmit authentication credentials. In this system, it carries the JWT token in the format Bearer <token> and is required on all protected endpoints.

bcrypt

A password hashing algorithm used in the Users service to store user credentials securely. Passwords are never stored in plain text; bcrypt applies a salt and a configurable cost factor before storing the hash.

Bot

An automated player controlled by the Gamey engine. The system supports four bot strategies: random (Random — picks any valid cell), defensive (Defensive — path-aware strategy), ai (AI powered by Gemini), and mcts (Monte Carlo tree search). Exposed publicly via GET /play.

bot_id

The public-facing identifier for a bot strategy used in the external API (GET /play). Maps to internal Gamey strategy names: random → Random, defensive → Defensive, ai → AI (Gemini), mcts → Monte Carlo.

CI/CD

Continuous Integration / Continuous Deployment. Automated pipelines (GitHub Actions) that build, test, and deploy the application on every push to the repository.

coordinates

A pair [row, col] identifying a cell on the triangular Game Y board, where row ranges from 0 to size-1 and col ranges from 0 to row. Returned by Gamey after every bot move.

CORS (Cross-Origin Resource Sharing)

An HTTP mechanism that controls which origins are permitted to call the Users service API from a browser. Configured in the Users service middleware to allow requests from the frontend origin.

crypto.randomInt

A Node.js built-in cryptographically secure random integer generator. Used in the Users service to assign the starting turn (B or R) at the beginning of each game.

current_turn

A field stored on each game document in MongoDB indicating whose turn it is ("B" or "R"). Updated by the Users service after every valid move; the frontend reads it to determine which player should act next.

difficulty_level

A label derived from the bot strategy that describes the challenge level presented to the player: random → Random, defensive → Defensive, ai → AI (Gemini), mcts → Monte Carlo.

Docker Compose

A tool for defining and running multi-container Docker applications using a single docker-compose.yml configuration file. Used in this project to orchestrate the webapp, users, gamey, and mongodb containers.

DFS (Depth-First Search)

A graph traversal algorithm used by the Gamey engine to detect win conditions. After each move it traverses the board from a player’s pieces to determine whether they form a connected chain touching all required edges of the triangular board.

Express

A minimal Node.js web framework used to build the Users service REST API. Routes are organised into separate modules (authRoutes, userRoutes, gameRoutes, playRoute, leaderBoardRoute).

Gamey

The Rust-based game engine service running on port 4000. Responsible for all game logic: move computation, win detection, bot strategy execution, and YEN state management. Has no database connection; all state is in memory during a match.

game_type

A field on the game document indicating whether the game is played against another human ("PLAYER") or against a bot ("BOT"). Determines which branch of updateStats is executed when the game finishes.

GitHub Actions

The CI/CD platform used to automate testing and deployment of the YOVI_EN1B project on every push to the repository.

Grafana

A monitoring and visualisation tool connected to Prometheus. Used to display real-time dashboards of system health and performance metrics for the YOVI platform.

JWT (JSON Web Token)

A compact, self-contained token used for authentication. Issued by the Users service on login and sent by the client as a Bearer token on all protected requests. Contains the user ID and expires after 24 hours.

leaderboard

A ranked list of players by performance. The Users service exposes GET /leaderboard returning an overall ranking by total wins and three vs_bots rankings (one per bot strategy).

lean()

A Mongoose method that returns plain JavaScript objects (POJOs) instead of full Mongoose documents. Used in getLeaderboard for performance; requires optional chaining (?.) because subdocuments may be absent on older records.

MongoDB

A document-oriented NoSQL database used for persistent storage of user accounts, authentication credentials, statistics, and game history. Accessed exclusively by the Users service via Mongoose.

Mongoose

An ODM (Object Document Mapper) library for MongoDB and Node.js. Provides schema definitions, validation, and query helpers used throughout the Users service.

mongo-data

The named Docker volume that persists MongoDB data across container restarts. Removed with docker compose down -v.

move

A single action taken by a player during a game, identified by coordinates [row, col] on the triangular board. Stored in the moves array of the game document along with the resulting yen_state.

nock

A Node.js HTTP interception library used in tests to mock outgoing HTTP calls from the Users service to Gamey, without requiring a real Gamey instance to be running.

Prometheus

A time-series monitoring system that scrapes metrics from the Users service. Works alongside Grafana to provide operational visibility into request rates, error rates, and response times.

repository pattern

An architectural pattern used in the Users service to abstract database access. MongoUserRepository implements UserRepository, making it easy to swap the storage backend (e.g., for testing with an in-memory store).

result

The outcome of a finished game from the perspective of the human player: "WIN", "LOSS", or "SURRENDER". SURRENDER (user surrendered) does not update statistics.

SonarCloud

A static analysis service used to enforce code quality standards on the repository. Acts as a quality gate requiring ≥80% coverage on new code and zero critical issues before merging.

SPA (Single Page Application)

A web application architecture where the browser loads a single HTML page and dynamically updates content via JavaScript. The YOVI frontend is a React SPA built with Vite and TypeScript, served on port 80.

strategy

The internal Gamey identifier for a bot algorithm. Valid values are random (Random), defensive (Defensive), ai (AI powered by Gemini), and mcts (Monte Carlo). Stored on the game document and used as the key in statistics.vs_bot.

triangular board

The Game Y playing surface. For board size n, it contains n*(n+1)/2 cells arranged in rows of increasing length (row r has r+1 cells). Encoded in YEN layout notation using / as row separators.

Users service

The Node.js/Express backend service running on port 3000. Responsible for user registration, authentication, game management, statistics tracking, leaderboard, and the public bot API (GET /play).

VM (Virtual Machine)

The Azure cloud virtual machine (IP 4.233.184.98) hosting all Docker containers for the YOVI platform in production.

winner

A field returned by Gamey’s /compute and /play endpoints after every move. Contains "B", "R", or null. When non-null, the Users service automatically finishes the game and updates statistics.

yen_state

The serialized board state in YEN (Yovi Extended Notation) format. Stored with each move and passed between the frontend, Users service, and Gamey to reconstruct the board at any point in time.

YEN (Yovi Extended Notation)

The notation format used to encode Game Y board states. A layout string where rows are separated by /, each row has r+1 characters (. for empty, B or R for pieces), and the full position is represented as a JSON object with size, turn, players, and layout fields.

YGN (Yovi Game Notation)

A notation format supported by Gamey for encoding full move sequences, enabling game export and replay functionality.