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.
1. Introduction and Goals
YOVI is a web platform for playing the game Y, developed by Micrati.
The system is designed for both human users and automated agents (bots). Its primary goal is to provide a smooth and engaging user experience when playing against an Artificial Intelligence (AI), with configurable board sizes and selectable difficulty levels.
YOVI consists of a React-based web frontend (running in the browser), an Auth Service (managing authentication and credentials), a Users Service (managing user profile data), a Game Service (handling match persistence and statistics), and a Rust-based Game Server (handling game logic). All backend services communicate via REST APIs using JSON messages, with YEN notation for game states, and are accessible through a single Nginx API Gateway.
This section summarizes the most relevant functional requirements from an architectural perspective.
1.1. Requirements Overview
The following table summarizes the main use cases of the system:
| Primary Actors | Use Case / Functionality | Description |
|---|---|---|
Player |
Play Game |
The player accesses the web application in their browser and starts a new game of Y. The React frontend displays a board with configurable size. The player takes turns placing pieces on the board. After each player move, the frontend sends the current game state in YEN notation to the Game Server via Nginx. The AI responds with its move, which is applied to the board and shown to the player. The Game Service persists each move in |
Player |
Strategy Configuration |
Before or during a game, the player selects a game strategy for the AI and an associated difficulty level through the React UI. The system stores this configuration in the Game Service and uses it whenever it requests the next move from the Game Server. The game state and selected strategy are sent to the Rust Game Server, which calculates the move accordingly. |
Registered Player |
History and Statistics Management |
A registered player accesses their profile within the web application. The React frontend requests statistics from the Game Service via Nginx. The system retrieves information associated with the user from |
Player |
User Registration and Authentication |
A new player registers through the React frontend. The Auth Service receives the registration request, hashes the password, and stores the credentials in |
External Bot |
API for External Bots |
An external bot makes requests to the system API through Nginx to create games, check board state, or make moves. The bot sends the game state using YEN notation to the Game Server and receives responses in the same format. Authentication is handled by the Auth Service against |
System (Game Server) |
Move Validation and Suggestion |
The Game Server receives the current game state in YEN notation from the React frontend (via Nginx). The Rust module determines if the game has ended and, if not, calculates the optimal next move according to the selected strategy. The result is returned to the frontend to update the game board. The Game Server is completely stateless and has no database access. |
1.2. Quality Goals
The following table outlines the key quality attributes that are most important for the architecture of the YOVI system
| Quality Attribute | Description |
|---|---|
Performance |
The system must calculate moves and check victory conditions in suitable timeframes to ensure a smooth gameplay experience, even on variable-size boards. |
Scalability |
The system must support multiple concurrent games and users, allowing growth without major architectural redesigns. |
Maintainability |
The architecture should facilitate adding new strategies, Y game variants, or logic changes without impacting the rest of the system. |
Interoperability |
The system must allow integration with external clients through a well-defined, documented, and interface-independent API. |
Usability |
The web application should offer a clear and intuitive interface, enabling users to play games and consult information effortlessly. |
Security |
The system should protect backend services from direct external access, handle authentication properly, and ensure data integrity through controlled and isolated database access per service. |
1.3. Stakeholders
The table below identifies the main stakeholders of the YOVI system, along with their roles, contacts, and expectations regarding the architecture and the delivered solution.
| Role/Name | Contact | Expectations |
|---|---|---|
Micrati (Client) |
Micrati Company |
Delivery of a functional system that meets all requirements. |
Development Team |
UO302313@uniovi.es – David Fernando Bolaños Lopez UO294946@uniovi.es – Raúl Velasco Vizán UO301919@uniovi.es – Ángela Nistal Guerrero UO300731@uniovi.es – Olai Navarro Baizán UO301831@uniovi.es – Alejandro Requena Roncero |
Develop a robust solution, well-documented and easy to integrate. |
Players |
Application Users |
An attractive web interface, ability to check personal statistics, and AI/bots that provide a real challenge with fast response times and selectable difficulty levels. |
Professors |
Technical quality in code, a functional project, and coherent architectural documentation. |
2. Architecture Constraints
Before starting the design and implementation of YOVI, it is important to be aware of the constraints that will guide architectural decisions. These constraints reflect organizational limitations, technical choices, and mandatory practices that the development team must follow. Understanding them helps ensure that the architecture is feasible, maintainable, and aligned with project requirements.
2.1. Organizational Constraints
The following organizational constraints define the environment in which the YOVI system is developed.
| Constraint | Explanation |
|---|---|
Small team of 5 people |
The entire solution must be developed and maintained by a small team, so simplicity, modularity, and ease of integration are prioritized. |
Delivery deadlines |
The system must meet the deadlines set by the ASW project, including partial and final submissions. |
Mandatory testing |
Unit, integration, and end-to-end tests must be performed during development to ensure system quality. |
2.2. Technical Constraints
Technical constraints specify mandatory technologies, languages, and communication protocols that the system must adopt.
| Constraint | Explanation |
|---|---|
Frontend language: TypeScript |
The web application must be implemented in TypeScript, so client-side logic must adhere to this ecosystem. |
Frontend framework: React |
The web interface must be built using React, which influences component structure and state management. |
Game logic module language: Rust |
Victory checking and move suggestion are implemented in Rust, constraining the communication interface and available libraries. |
Communication via JSON / YEN notation |
All exchanges between the web application and the game logic module, as well as with the bot API, must use JSON messages in YEN format. |
Mandatory web deployment |
The application must be deployed and publicly accessible, influencing architecture design for web hosting and basic scalability. |
Data persistence |
Storage of users, games, and statistics is required, affecting database selection and backend organization. |
3. Context and Scope
Yovi is a distributed web application that allows users and bots to play the Y board game. The system provides a platform where human players can compete against each other or against AI bots, with match history and statistics tracking for registered users.
3.1. Business Context
3.1.1. Communication Partners
| Partner | Input | Output |
|---|---|---|
Human Player (Web User) |
|
|
Bot Client (External/Internal) |
|
|
Administrator |
|
|
3.1.2. Domain Concepts
-
YEN (Y-encoded Notation): Domain-specific format for representing board positions and moves in the Y game
-
Match: A game session between two players (human or bot)
-
Strategy: Game difficulty level and bot behavior configuration
-
Anonymous Play: Unregistered users can play but their scores are not persisted
3.2. Technical Context
3.2.1. System Architecture
3.2.2. Technical Interfaces
| Component Connection | Protocol/Technology | Port/Channel | Purpose |
|---|---|---|---|
Web Browser → Nginx (API Gateway) |
HTTP, REST API |
80 (HTTP) |
|
Nginx → Auth Service |
HTTP, REST API |
3001 (HTTP) |
|
Nginx → Users Service |
HTTP, REST API |
3000 (HTTP) |
|
Nginx → Game Service |
HTTP, REST API |
3002 (HTTP) |
|
Nginx → Game Server |
HTTP, REST API |
4000 (HTTP) |
|
Auth Service → auth.db |
SQLite native (embedded) |
Local file access |
|
Users Service → users.db |
SQLite native (embedded) |
Local file access |
|
Game Service → game.db |
SQLite native (embedded) |
Local file access |
|
React Frontend (in Browser) → Nginx |
HTTP, REST API, AJAX |
80 (HTTP) |
|
3.2.3. Key API Endpoints
Authentication (via Nginx → Auth Service):
-
POST /api/auth/register- User registration -
POST /api/auth/login- User authentication, returns JWT token -
POST /api/auth/refresh- JWT token refresh -
POST /api/auth/verify- JWT verification endpoint for internal service-to-service authorization checks (must not be exposed publicly)
User Profile Management (via Nginx → Users Service):
-
GET /api/users/{id}- Retrieve user profile -
PUT /api/users/{id}- Update user profile -
GET /api/users- Search users
Game Data Management (via Nginx → Game Service):
-
POST /api/game/matches- Create new match (size, strategy, difficulty) -
GET /api/game/matches/{id}- Retrieve match state -
POST /api/game/matches/{id}/moves- Persist a move and updated YEN state -
GET /api/game/stats/{userId}- Retrieve player statistics and match history
Game Logic (via Nginx → Game Server):
-
POST /api/gamey/play- Submit player move (position=YEN, strategy, difficulty)-
Returns: Updated YEN board state, move validation status
-
-
POST /api/gamey/ybot/choose/{botId}- Bot move computation-
Returns: YenMoveDto + game status
-
-
GET /api/gamey/validate- Validate move without executing
Bot API (External bots via Nginx):
-
POST /api/gamey/bot/play- Bot move submission (position=YEN, strategy, difficulty)
3.2.4. Data Formats
YEN Notation Example:
{
"size": 4,
"turn": "R",
"players": ["B", "R"],
"layout": "B/.B/RB./B..R"
}
Move Request/Response:
{
"position": "B/.B/RB./B..R",
"strategy": "heuristic",
"difficulty": 3,
"nextMove": {
"player": "R",
"coordinate": {"x": 0, "y": 1, "z": 2}
}
}
3.2.5. Deployment Considerations
-
Containerization: All services (React Frontend, Nginx, Auth Service, Users Service, Game Service, Game Server) are containerized using Docker for consistent deployment
-
Databases: Three independent SQLite files (
auth.db,users.db,game.db), each stored as a separate volume mount in its respective service container -
Fault Isolation: A failure or overload in one database does not affect the others; authentication, profile, and game data are independently available
-
Scalability: Game Server is stateless and can be horizontally scaled behind Nginx load balancer; each Node.js service can also be scaled independently
-
Security: Nginx handles CORS, rate limiting, and request validation; no backend service is directly accessible from outside
-
API Gateway: Nginx isolates all backend services from direct external access
-
Frontend Deployment: React app is built and served as static files through Nginx
4. Solution Strategy
4.1. Overview
This section outlines the fundamental architectural and technological decisions that shape the Yovi system. These decisions were made based on project requirements, quality goals, and technical constraints imposed by the academic context.
4.2. Technology Decisions
4.2.1. Distributed Architecture with Microservices
| Decision | Rationale |
|---|---|
Nginx as API Gateway |
|
Separate Game Server (Rust) |
|
Auth Service (Node.js + Express + TypeScript) |
|
Users Service (Node.js + Express + TypeScript) |
|
Game Service (Node.js + Express + TypeScript) |
|
Frontend (React + Vite + TypeScript) |
|
Databases (SQLite x3) |
|
4.3. Architectural Patterns
4.3.1. Microservices Architecture
The system follows a microservices architecture where:
-
Nginx acts as the API Gateway, providing a single entry point and routing requests to appropriate services
-
React Frontend runs in the user’s browser and coordinates API calls to backend services
-
Auth Service is exclusively responsible for authentication, registration, and JWT management, backed by
auth.db -
Users Service manages user profile data, backed by
users.db -
Game Service manages all game-related persistence (matches, moves, statistics), backed by
game.db -
Game Server remains stateless and focuses solely on game domain logic
-
YEN notation defines the boundaries between services for game state communication
-
Each service owns its own database, ensuring fault isolation: a failure or overload in one database does not propagate to the others
4.3.2. Layered Architecture within Services
React Frontend Layers:
-
Components: React UI components for board, moves, statistics
-
State Management: Local game state and user session (React hooks/context)
-
API Layer: Abstraction over backend API calls
-
HTTP Client: Fetch/Axios for making HTTP requests to Nginx
Auth Service Layers:
-
AuthController: Handles HTTP requests for registration and login
-
AuthService: Business logic for password hashing and JWT management
-
CredentialsRepository: Abstracts access to
auth.db
Users Service Layers:
-
UsersController: Handles HTTP requests for user profile operations
-
UserService: Business logic for user profile management
-
UserRepository: Abstracts access to
users.db
Game Service Layers:
-
GameController: Handles HTTP requests for match and stats operations
-
MatchService: Business logic for match creation, move persistence, and lifecycle
-
StatsService: Aggregates and retrieves player statistics
-
MatchRepository / StatsRepository: Abstract access to
game.db
Game Server Design:
-
Domain-Driven Design: Game, Board, Player, Move as core entities
-
Strategy Pattern: Multiple bot strategies with configurable difficulty levels
-
Encapsulation: Pure domain logic with no external dependencies
4.4. Quality Goals Achievement
| Quality Goal | Strategy | Implementation |
|---|---|---|
Performance |
|
|
Scalability |
|
|
Maintainability |
|
|
Interoperability |
|
|
Security |
|
|
4.5. Key Constraints
| Constraint | Impact on Architecture |
|---|---|
Mandated Technologies |
TypeScript for frontend/backend and Rust for game engine are project requirements, limiting technology choices |
YEN Notation |
All game state communication must use YEN format, requiring parsing/serialization layers |
Bot API Requirement |
System must expose external API for bot clients, influencing authentication and API design |
Academic Timeline |
Limited development time favors familiar technologies (React, Express) and simple deployment (SQLite) |
Web Deployment |
Application must be publicly accessible, requiring containerization and reverse proxy strategy |
Multiple Strategies |
Game engine must support extensible bot strategies, leading to Strategy pattern adoption |
Database Fault Isolation |
Three independent databases (auth.db, users.db, game.db) ensure that overload or failure in one domain does not affect the others |
4.6. Organizational Decisions
-
Monorepo structure: All services in one repository (
webapp/,users/,auth/,gamey/,gameservice/,nginx/) for easier coordination -
Docker Compose: Simplified local development with single command startup
-
Shared YEN notation: Common understanding of game state format across all services
-
API-first approach: Define API contracts before implementation
-
Version control: Git with feature branch workflow and code reviews
-
Documentation: arc42 for architecture, inline code documentation, README files per service
5. Building Block View
5.1. Whitebox Overall System
5.1.1. Overview Diagram
Motivation:
The Yovi system is decomposed into five main subsystems to achieve separation of concerns, independent scalability, technology-specific optimization, and fault isolation across data domains:
-
Nginx (API Gateway): Single public entry point for all requests, handling routing, rate limiting, and CORS
-
React Frontend (Webapp): User interface layer running in the browser, managing UI state and coordinating API calls to backend services
-
Auth Service: Authentication layer with exclusive access to
auth.db, managing credentials, registration, login, and JWT tokens -
Users Service: Profile layer with exclusive access to
users.db, managing user profile data -
Game Service: Game data layer with exclusive access to
game.db, managing match persistence, move history, and statistics -
Game Server: Pure game logic engine optimized for performance (Rust), completely stateless
Each service owns its own database. A failure or overload in one database does not propagate to the others, achieving fault isolation at the data layer.
Contained Building Blocks:
| Building Block | Responsibility |
|---|---|
Nginx (API Gateway) |
|
React Frontend (Webapp) |
|
Auth Service |
|
Users Service |
|
Game Service |
|
Game Server (Rust) |
|
auth.db (SQLite) |
|
users.db (SQLite) |
|
game.db (SQLite) |
|
Important Interfaces:
-
User → Nginx: HTTP REST API (port 80, public)
-
Nginx → React Frontend: Static file serving (HTML, JS, CSS)
-
Nginx → Auth Service: REST API (
/api/auth/*, port 3001, internal) -
Nginx → Users Service: REST API (
/api/users/*, port 3000, internal) -
Nginx → Game Service: REST API (
/api/game/*, port 3002, internal) -
Nginx → Game Server: REST API (
/api/gamey/*, port 4000, internal) -
React Frontend → Nginx: AJAX/Fetch API calls from browser
-
Auth Service → auth.db: SQLite native SQL queries
-
Users Service → users.db: SQLite native SQL queries
-
Game Service → game.db: SQLite native SQL queries
Communication Flow Example:
-
User loads the app → Nginx serves React static files → React runs in browser
-
User clicks "Login" → React calls
POST /api/auth/login→ Nginx routes to Auth Service -
Auth Service validates credentials against
auth.db→ Returns JWT token -
User creates a match → React calls
POST /api/game/matches→ Nginx routes to Game Service → persisted ingame.db -
User makes a move → React calls
POST /api/gamey/play→ Nginx routes to Game Server → returns updated YEN -
Game Server returns move validation → React calls
POST /api/game/matches/{id}/moves→ Game Service persists ingame.db -
React updates UI
5.2. Level 2
5.2.1. White Box: Auth Service
Contained Building Blocks:
| Component | Responsibility |
|---|---|
AuthController |
|
AuthService |
|
CredentialsRepository |
|
5.2.2. White Box: Users Service
Contained Building Blocks:
| Component | Responsibility |
|---|---|
UsersController |
|
UserService |
|
UserRepository |
|
5.2.3. White Box: Game Service
Contained Building Blocks:
| Component | Responsibility |
|---|---|
GameController |
|
MatchService |
|
StatsService |
|
MatchRepository |
|
StatsRepository |
|
5.2.4. White Box: Game Server
Motivation:
The Game Server encapsulates all game-specific logic using Domain-Driven Design principles. The Strategy pattern allows for extensible bot implementations without modifying core game logic. This service is completely stateless and has no database dependencies. All persistence is delegated to the Game Service.
Contained Building Blocks:
| Component | Responsibility |
|---|---|
EngineService |
|
NextMoveComputer |
|
WinEvaluator |
|
Strategy Interface |
|
RandomStrategy |
|
HeuristicStrategy |
|
GameDomain Package |
|
6. Runtime View
This section describes how the main components of the YOVI system collaborate at runtime to support user authentication, match management, gameplay, bot interaction, and statistics retrieval. The scenarios below are derived directly from the system’s architecture and message flows, using JSON/YEN as the canonical representation of game states.
All communication between the React Frontend (running in the user’s browser) and backend services goes through the Nginx API Gateway, which routes requests to the appropriate service based on URL paths.
6.1. Runtime Scenario 1: User Registration and Login
This scenario shows how a human user registers or logs into the system through the React Frontend.
The frontend makes API calls through Nginx, which routes authentication requests to the Auth Service, which persists credential data in auth.db.
Notable Aspects:
-
Authentication is fully isolated in the Auth Service and its dedicated
auth.db -
Nginx routes all
/api/auth/*requests to Auth Service (port 3001) -
React Frontend stores the JWT token (localStorage or sessionStorage)
-
auth.dbacts as the single source of truth for credentials -
A failure in other databases (
users.db,game.db) does not affect the login flow
6.2. Runtime Scenario 2: Authorization Check
When a user requests protected resources, the React Frontend includes the JWT token in the request headers.
Nginx forwards the request to the appropriate service, which delegates JWT verification to Auth Service through an internal call to /api/auth/verify.
Notable Aspects:
-
JWT validation is responsibility of the Auth Service
-
Other services (Game Service, Users Service) delegate token verification to Auth Service via internal call
-
React Frontend handles unauthorized responses by redirecting to login
-
Nginx simply routes requests without authentication logic
Verify endpoint contract used by internal services:
-
Method and path:
POST /api/auth/verify -
Internal URL (Docker network):
AUTH_INTERNAL_VERIFY_URL=http://auth:3001/api/auth/verify -
Token input: preferred
Authorization: Bearer <accessToken>(compatible fallback: JSON body{ "token": "<accessToken>" }) -
Success response (
200):{ "valid": true, "claims": { "sub": "<userId>", "username": "<username>", "tokenType": "access", "iat": <epoch>, "exp": <epoch> } } -
Error response (
401):{ "valid": false }for missing, malformed, invalid, expired, or non-access tokens -
Security boundary:
/api/auth/verifymust not be exposed publicly through Nginx and should be consumed only over internal service-to-service network calls -
Client resilience policy for internal callers: use short timeouts (
500msto1s) and0-1retries to avoid cascading failures
6.3. Runtime Scenario 3: Match Creation and Initial State Retrieval
A user creates a new match by specifying board size, strategy, and difficulty through the React UI.
The Game Service persists the match in game.db and returns the initial YEN state.
Notable Aspects:
-
The Game Service owns match lifecycle and persistence in
game.db -
Initial game state is always stored and returned in YEN format
-
React Frontend renders the game board based on received YEN notation
-
Match creation requires authentication (JWT token verified against Auth Service)
-
auth.dbandusers.dbare not involved in this flow
6.4. Runtime Scenario 4: Player Move (Human Turn)
The human player submits a move by clicking on the game board in React. The frontend sends the current game state to the Game Server for bot move computation, then persists the updated state in the Game Service.
Notable Aspects:
-
React Frontend coordinates the multi-step flow from the browser
-
Game Server (Rust) is stateless and only computes moves based on YEN input, with no DB access
-
Game Service is the authoritative source of match state persistence in
game.db -
All updates are persisted atomically in
game.db -
auth.dbandusers.dbare not involved in this flow
6.5. Runtime Scenario 5: Bot API Interaction
External bots can play matches programmatically using the API. The flow mirrors the human move scenario but uses bot credentials for authentication against the Auth Service.
Notable Aspects:
-
Bot authentication is handled exclusively by Auth Service against
auth.db -
Game data (match state, moves) is managed exclusively by Game Service against
game.db -
External bots must follow the same YEN notation format as human players
-
A failure in
users.dbdoes not affect bot gameplay
6.6. Runtime Scenario 6: Statistics and History Retrieval
Users can request aggregated statistics such as number of matches, wins/losses, and performance metrics through the React UI.
Statistics are served by the Game Service from game.db.
Notable Aspects:
-
Statistics are computed from persisted match data in
game.db -
The Game Service exposes a unified Stats API
-
React Frontend handles visualization (charts, tables)
-
All stats queries require authentication (JWT verified against Auth Service)
-
auth.dbandusers.dbare not involved in this flow
7. Deployment View
The deployment view describes the technical infrastructure we use to run the Yovi system. It also shows how the software parts (building blocks) connect to that infrastructure.
Because our system uses Docker containers, we describe the deployment in two main environments: the Production Environment and the Development Environment.
7.1. Infrastructure Level 1 - Production Environment
This section describes the main production environment where the final users can access the system.
Overview Diagram
Motivation
The main goal of this deployment strategy is to find a good balance between working fast and keeping the system secure. Software needs hardware to run. By using a Virtual Machine (VM) as the main host, the system creates a secure border at the hardware level. Inside this VM, we use Docker containers to run the apps. This makes sure that the deployment is isolated and prevents problems with installed programs on the host machine.
Quality and/or Performance Features
-
Security (Defense in Depth): The infrastructure has two layers of isolation. If a container is attacked, the VM acts as an external wall that protects the physical server. Also, the Nginx API Gateway is the only part connected to the public internet.
-
Portability: By using Docker containers, the environment is always the same. The application works exactly the same way, no matter what physical server is running the Virtual Machine.
-
Independent Scalability: The system is divided into specific microservices (React Frontend, Auth Service, Users Service, Game Service, Game Server). Because of this, developers can give more resources to a specific service without affecting the others.
-
Fault Isolation: Each service owns its own database volume. An overload or failure in
game.dbdoes not affectauth.dborusers.db, meaning authentication and profile access remain available even if the game data layer is under stress.
Mapping of Building Blocks to Infrastructure
| Software Artifact (Building Block) | Infrastructure Element |
|---|---|
React Frontend (Webapp) |
Deployed as static files (HTML, CSS, JS bundle) served by Nginx on port 80. The React application runs entirely in the user’s browser. Built with Vite and bundled for production. |
Nginx (API Gateway) |
Deployed in a Docker container inside the VM. It works as a Reverse Proxy and API Gateway. It is the only public entry point (Port 80) to route all incoming traffic. Handles CORS, rate limiting, and serves the React frontend static files. Routes API requests to backend services. |
Auth Service (Node.js + Express + TypeScript) |
Deployed in a Docker container inside the VM (Port 3001, internal only). Exclusively manages user credentials, registration, authentication, and JWT token lifecycle. No other service has access to |
Users Service (Node.js + Express + TypeScript) |
Deployed in a Docker container inside the VM (Port 3000, internal only). Exclusively manages user profile data. No other service has access to |
Game Service (Node.js + Express + TypeScript) |
Deployed in a Docker container inside the VM (Port 3002, internal only). Exclusively manages match persistence, move history, and player statistics. No other service has access to |
Game Server (Rust) |
Deployed in a Docker container inside the VM (Port 4000, internal only). Calculates game logic and bot movements in YEN format. Completely stateless service optimized for performance. No database access whatsoever. |
auth.db (SQLite) |
A database file saved on a dedicated Host Volume inside the VM, connected exclusively to the Auth Service container. Stores user credentials and JWT token metadata. Only Auth Service has read/write permissions. |
users.db (SQLite) |
A database file saved on a dedicated Host Volume inside the VM, connected exclusively to the Users Service container. Stores user profile data. Only Users Service has read/write permissions. |
game.db (SQLite) |
A database file saved on a dedicated Host Volume inside the VM, connected exclusively to the Game Service container. Stores matches, move history, and statistics. Only Game Service has read/write permissions. |
7.2. Infrastructure Level 1 - Development Environment
Following the arc42 rules for multiple environments, the development environment uses the same Docker containers to be equal to production, but without the secure Virtual Machine.
Motivation
Developers need a fast and reliable environment to write and test code on their computers. We use Docker Compose to start the whole system on the developer’s local machine.
Quality and/or Performance Features
-
Consistency: It solves the classic "it works on my machine" problem.
-
Fast Setup: A new developer can start the complete system (React Frontend, Nginx, Auth Service, Users Service, Game Service, Game Server, and all three databases) with just one
docker-compose upcommand.
Mapping of Building Blocks to Infrastructure
In this environment, the mapping is exactly the same as in Production. The only difference is the host hardware: * Infrastructure Element: The developer’s personal computer (Localhost) running Docker Desktop, instead of a Cloud Virtual Machine.
7.3. Infrastructure Level 2
Here we explain the internal structure of the Docker environment from Level 1. We zoom into the isolated network and how data is saved.
Internal Structure Explanation:
This level shows the internal Docker network rules. The Nginx API Gateway completely isolates the microservices from the public internet. External users can never reach the backend services directly. All requests must go through Nginx on port 80.
The request flow is as follows:
-
User browser loads the app: Nginx serves React static files (HTML, CSS, JS)
-
React app runs in browser: Makes API calls to
/api/auth/,/api/users/,/api/game/and/api/gamey/ -
Nginx routes API requests based on URL path:
-
/api/auth/*→ Auth Service (port 3001), except/api/auth/verify(blocked externally with 403) -
/api/users/*→ Users Service (port 3000) -
/api/game/*→ Game Service (port 3002) -
/api/gamey/*→ Game Server (port 4000) -
/*(all other paths) → React frontend static files
-
Example User Flow:
-
User opens
http://yourapp.comin browser -
Nginx serves
index.htmland React bundle from static files -
React app loads in browser, displays login screen
-
User clicks "Login" → React sends
POST /api/auth/loginto Nginx -
Nginx routes request to Auth Service (port 3001)
-
Auth Service validates credentials against
auth.dband returns JWT token -
React stores token and creates a new match → React sends
POST /api/game/matchesto Nginx -
Nginx routes request to Game Service (port 3002)
-
Game Service persists the new match in
game.dband returns the match ID -
User makes a move → React sends
POST /api/gamey/playto Nginx -
Nginx routes game logic request to Game Server (port 4000)
-
Game Server computes the bot move and returns the updated YEN state
-
React persists the move → sends
POST /api/game/matches/{id}/movesto Nginx -
Nginx routes to Game Service (port 3002), which stores the move in
game.db -
React updates the UI with the new board state
Internal Auth verification call (service-to-service only):
-
Internal URL env var:
AUTH_INTERNAL_VERIFY_URL=http://auth:3001/api/auth/verify -
Recommended timeout in callers:
500msto1s -
Recommended retries:
0-1(avoid retry storms)
Data Storage and Fault Isolation:
Data storage is strictly controlled through three independent volumes, all inside the VM:
-
auth.dbis mounted exclusively inside the Auth Service container. Only Auth Service has read/write permissions. Stores credentials and JWT metadata. -
users.dbis mounted exclusively inside the Users Service container. Only Users Service has read/write permissions. Stores user profile data. -
game.dbis mounted exclusively inside the Game Service container. Only Game Service has read/write permissions. Stores matches, moves, and statistics.
If game.db becomes overloaded due to a high volume of concurrent matches, authentication (auth.db) and user profiles (users.db) remain fully operational. Each database fails independently.
The Game Server has no database access and operates in a completely stateless manner. All game state persistence is delegated to the Game Service after each move.
Network Isolation:
All backend services (Auth Service, Users Service, Game Service, Game Server) run on an internal Docker network. They cannot be accessed directly from outside the VM. Only Nginx has a public-facing port (80) exposed to the internet. This architecture ensures that:
-
Backend services are protected from direct attacks
-
All requests go through Nginx security layers (rate limiting, CORS)
-
Each database is isolated behind its own service container
-
Services communicate only through defined internal APIs
8. Cross-cutting Concepts
8.1. Domain Model
A first version of the domain model of the application, representing all the entities that interact with the application and their relations, is shown in the following diagram:
8.2. User experience (UX)
Since the project includes a web interface, we wanted the user experience to be as smooth and intuitive as possible. The idea is that anyone can open the application and start playing without needing instructions.
Key UX ideas:
-
Visual Feedback: The interface reacts immediately to user actions (valid moves, turn changes, errors). This helps players understand what is happening in the game at all times.
-
Consistency: We try to keep the same style and interaction patterns across all pages so the user does not have to "relearn" how to use the app.
-
Accessibility: Clear colors, readable text, and simple layouts help make the game accessible to a wider audience.
8.3. Safety and Security Concepts
Security is a cross‑cutting concern because it affects both the backend services and the frontend. Even though this is an academic project, we still apply basic security principles.
-
API Gateway Isolation: Nginx acts as the single public entry point, isolating backend services from direct external access. Only Nginx is exposed on port 80, while Users Service and Game Server run on internal Docker network ports.
-
Authentication and Authorization: Only authenticated users can access certain features, such as creating matches or viewing their profile. The Auth Service handles JWT authentication and exposes token verification for internal service-to-service authorization checks.
-
Password Protection: Passwords are never stored in plain text. Instead, they are hashed and salted before being saved in the database using bcrypt or Argon2.
-
Data Protection: We only store the minimum amount of user information needed for the system to work. Data is split by domain with strict ownership:
auth.dbis accessible only through Auth Service,users.dbonly through Users Service, andgame.dbonly through Game Service. -
CORS and Rate Limiting: Nginx handles Cross-Origin Resource Sharing (CORS) headers and rate limiting to prevent abuse and ensure legitimate API usage.
8.4. Architecture and Design Patterns
The system follows a microservice-based architecture. Instead of having one big monolithic application, we split the project into several smaller, independent services:
-
React Frontend (webapp/): The user interface running in the browser. It is a single-page application (SPA) built with React, Vite, and TypeScript. The frontend manages UI state and coordinates API calls to backend services.
-
Nginx: The API Gateway and reverse proxy that routes requests to appropriate backend services. It handles CORS, rate limiting, and serves the React frontend static files.
-
Auth Service (auth/): Node.js + Express + TypeScript service that manages registration, authentication (JWT access/refresh lifecycle), and internal token verification. It is the single point of access to
auth.db. -
Users Service (users/): Node.js + Express + TypeScript service that manages user profiles, match persistence, and statistics. It is the single point of access to
users.db. -
Game Server (gamey/): Rust service that implements the Y‑game engine and bot logic. It is stateless and focuses purely on game rules, move validation, and bot strategies.
Some patterns we use:
API Gateway Pattern:
Nginx centralizes all external requests and routes them to the appropriate service based on URL paths (/api/users/, /api/gamey/, /).
Repository Pattern: The Users Service uses the Repository pattern to abstract database access, making it easier to test and maintain.
Strategy Pattern: The Game Server uses the Strategy pattern to implement multiple bot difficulty levels (random, heuristic) in an extensible way.
Separation of Concerns: The frontend separates UI components, state management (React hooks/context), and API call logic into distinct layers.
8.5. Development Concepts
These concepts describe the practices that the whole team follows during development.
Version Control:
The project is developed collaboratively using GitHub. Branching, pull requests and code reviews ensure that all components evolve consistently.
Testing:
Each part of the system includes automated tests:
-
Rust Game Server: unit tests for game logic and strategies
-
Users Service (Node.js): API endpoint tests and integration tests
-
React Frontend: component tests and end-to-end tests
Testing is transversal because it guarantees correctness across all services.
Continuous Integration:
GitHub Actions automatically run builds and tests whenever code is pushed. This helps detect errors early and keeps all services aligned.
8.6. Persistence Layer
The application relies on SQL databases (SQLite - relational database) split by domain: auth.db, users.db, and game.db.
Each database is accessed exclusively by its owning service. This design decision ensures:
-
Data Consistency: each service manages transactions in its own domain
-
Security: other services cannot directly access or modify another service’s database
-
Separation of Concerns: Auth handles credentials/tokens, Users handles profiles/statistics, and Game handles match state
Game sessions are computed in memory by the Rust Game Server and persisted to game.db when needed (history/resume). Credentials and token metadata remain exclusive to Auth Service in auth.db.
If additional persistence requirements arise in the future (for example, caching or match replay functionality), the system can be extended without affecting the existing services thanks to the microservice-based design.
8.7. Error Handling
The application includes a consistent error handling strategy to deal with the different types of errors that may appear during execution. The goal is to detect problems early, avoid unexpected crashes and provide clear feedback both to developers and to users.
At the user level, the application only displays simple and understandable messages, without exposing technical details. This prevents confusion and avoids leaking internal information. On the other hand, the backend services and the Rust game engine generate structured error messages and log them internally so developers can identify the cause of the problem more easily.
Error Handling Across Services:
-
React Frontend: Displays user-friendly error messages for API failures, invalid moves, or authentication errors. Uses try-catch blocks and error boundaries for React component errors.
-
Nginx: Returns standard HTTP error codes (400, 401, 404, 500) and logs errors for monitoring.
-
Users Service: Validates input, handles database errors, and returns appropriate HTTP status codes with error messages. Uses middleware for centralized error handling.
-
Game Server: Returns structured error responses for invalid YEN notation, illegal moves, or computation failures. Rust’s Result type ensures explicit error handling.
9. Architecture Decisions
9.1. ADR 1: Microservices architecture
-
Context: The platform is expected to scale with increasing user demand, evolve over time with new features or game modes, and support parallel development by multiple team members. High availability and maintainability are important non-functional requirements.
-
Status: Accepted by the teacher and development team.
-
Possible alternatives:
-
Monolithic architecture: where all game logic, API web and data storage services would be integrated in a single backend app. Although it would have simplified the development at first, this solution presents big issues in terms of scalability and maintainability.
-
-
Decision: We decided to implement a microservice-based architecture with a clear separation between game logic, data persistence, and presentation layers. The system will consist of:
-
Game Server (Rust) handling core game rules, move validation, and bot strategies
-
Auth Service (Node.js + Express + TypeScript) managing user authentication, registration, and JWT token lifecycle, with exclusive access to
auth.db -
Users Service (Node.js + Express + TypeScript) managing user profile data, with exclusive access to
users.db -
Game Service (Node.js + Express + TypeScript) managing match persistence, move history, and statistics, with exclusive access to
game.db -
React Frontend (Vite + TypeScript) for user interaction, running entirely in the browser and coordinating API calls to backend services
-
Nginx API Gateway routing requests and handling cross-cutting concerns (CORS, rate limiting, security)
-
Each service owns its own database, ensuring fault isolation at the data layer
This approach allows each component to evolve independently while using the most appropriate technology for its responsibilities.
-
-
Pros:
-
Clear separation of concerns between game logic, data persistence, and user interface
-
Enables independent development and deployment of all services
-
Each database is isolated per domain, ensuring fault isolation
-
Nginx provides security isolation for backend services
-
-
Cons:
-
Increased architectural complexity compared to a single monolithic backend
-
Introduces inter-service communication overhead and potential latency
-
Frontend must coordinate multiple API calls for complex workflows
-
9.2. ADR 2: Rust for Game Server
-
Context: The game server must be performant, safe, and reliable, as errors in the game logic could compromise fairness or stability. Additionally, the engine may need to handle multiple concurrent games and interact efficiently with other services in the system.
-
Status: Accepted by the teacher and development team.
-
Possible alternatives:
-
There were no possible alternatives because this was an imposed constraint, given by the course’s teachers.
-
-
Decision: We decided to implement the Game Server using Rust, which provides strong compile-time guarantees for memory safety and concurrency without relying on a garbage collector. This makes it well-suited for implementing complex game logic that must be both efficient and robust. The Game Server will be completely stateless, with no database access. All persistence is delegated to the Game Service.
-
Pros:
-
Strong memory safety guarantees, reducing runtime errors and crashes
-
High performance and excellent support for concurrency, which is important for managing game sessions in parallel
-
Stateless design enables horizontal scaling
-
-
Cons:
-
Steeper learning curve, as the development team is not familiar with this programming language
-
9.3. ADR 3: Docker Containerization
-
Context: The platform is composed of multiple independent components implemented with different technologies and runtimes. A way to ensure consistent development, testing, and deployment across different environments is essential to reduce configuration issues and simplify setup for new team members.
-
Status: Accepted by the teacher and development team.
-
Possible alternatives:
-
There were no possible alternatives because this was an imposed constraint, given by the course’s teachers.
-
-
Decision: We decided to containerize all major components of the system using Docker. Each service (React Frontend, Nginx, Auth Service, Users Service, Game Service, Game Server) will run inside its own Docker container, with Docker Compose used to orchestrate and manage the multi-container setup. This ensures environment consistency and simplifies deployment and execution of the full system.
-
Pros:
-
Provides consistent environments across development, testing, and deployment
-
Simplifies setup and onboarding by reducing manual configuration
-
Enables easier deployment and scaling of individual services
-
Isolated network for backend services through Docker networking
-
Each database file is mounted as an independent volume per service container
-
-
Cons:
-
Adds an extra layer of complexity to the development workflow
-
Requires basic knowledge of Docker and container orchestration from the development team
-
9.4. ADR 4: SQLite as Database System
-
Context: The application requires persistent storage for game data, user information, and match states. The database solution should be simple to operate, easy to integrate with the existing backend services, and appropriate for an academic project deployed in a cloud environment.
-
Status: Proposed by the development team.
-
Possible alternatives:
-
MongoDB: A document-oriented NoSQL database was considered. It would allow the development team to store game states in a more flexible way, using JSON structures. This alternative was not adopted as the development team was not familiar with this kind of DBs when the decision was taken.
-
-
Decision: We decided to use SQLite as the database system for all three data domains. Each service owns its own SQLite database file (
auth.db,users.db,game.db), mounted as an independent Docker volume. No service has access to another service’s database. This choice is also highly based on the development team’s experience using relational databases. -
Pros:
-
Easy integration with backend services and containerized environments
-
Suitable for small-to-medium workloads
-
Low learning curve, as the development team is used to working with relational databases
-
Each service owns its own database file, enforcing data isolation
-
Embedded database requires no separate server process
-
-
Cons:
-
Limited scalability and concurrency support compared to cloud-native database solutions
-
Not ideal for high-availability or horizontally scaled write-heavy workloads
-
9.5. ADR 5: Nginx as API Gateway
-
Context: The system requires a secure entry point for all external requests. Backend services (Auth Service, Users Service, Game Service, and Game Server) should not be directly accessible from the internet. Additionally, cross-cutting concerns such as CORS, rate limiting, and request routing need to be handled consistently.
-
Status: Proposed by the development team.
-
Possible alternatives:
-
Direct access to backend services: Each service could expose its own public port, but this would increase security risks and make it harder to manage cross-cutting concerns.
-
Cloud API Gateway services (AWS API Gateway, Azure API Management): These would provide more features but add complexity and cost for an academic project.
-
-
Decision: We decided to use Nginx as a reverse proxy and API Gateway. Nginx will be the only component with a publicly exposed port (80). It will route requests based on URL paths:
-
/api/auth/*→ Auth Service (port 3001, internal) -
/api/users/*→ Users Service (port 3000, internal) -
/api/game/*→ Game Service (port 3002, internal) -
/api/gamey/*→ Game Server (port 4000, internal) -
/*→ React Frontend static files Nginx will also handle CORS headers, rate limiting, and request logging.
-
-
Pros:
-
Single public entry point enhances security (defense in depth)
-
Backend services are isolated on internal Docker network
-
Centralized handling of CORS, rate limiting, and logging
-
High performance and battle-tested in production environments
-
Simple configuration and widely known by the development community
-
-
Cons:
-
Adds an additional component to the system architecture
-
Requires learning Nginx configuration syntax
-
Single point of failure (mitigated by its reliability)
-
9.6. ADR 6: Three Independent Databases for Fault Isolation
-
Context: The initial architecture used a single SQLite database accessed exclusively by the Users Service. As the system evolved and the number of concurrent users and game sessions grew, a concern arose: a single overloaded or failed database would bring down all data-dependent functionality simultaneously — authentication, user profiles, and game data would all fail together.
-
Status: Accepted by the teacher and development team.
-
Possible alternatives:
-
Single database: One SQLite file accessed by one service. Simple but creates a single point of failure at the data layer. If the database is locked or overloaded (e.g., by a high volume of match writes), authentication also fails.
-
Single service with multiple internal connections: A single backend service managing three internal database connections. This keeps one entry point but does not achieve true fault isolation at the service level — if the service crashes, all three databases become inaccessible anyway.
-
-
Decision: We decided to split the data layer into three independent services, each owning its own SQLite database:
-
Auth Service →
auth.db: stores credentials and JWT token metadata -
Users Service →
users.db: stores user profile data -
Game Service →
game.db: stores matches, move history, and statistics Each database is mounted as an independent Docker volume, and no service has access to another service’s database.
-
-
Pros:
-
Fault isolation: a failure or overload in
game.db(e.g., caused by a high volume of concurrent matches) does not affect authentication or user profile access -
Independent scalability: each service and its database can be scaled according to its own load profile
-
Single responsibility: each service has a clearly defined data domain, improving maintainability
-
-
Cons:
-
Queries that previously joined data across domains (e.g., linking statistics to a user profile) now require inter-service HTTP calls, adding latency and complexity
-
Three services to maintain instead of one, increasing development and operational overhead
-
Cross-service consistency (e.g., deleting a user requires coordinating Auth Service, Users Service, and Game Service) must be handled at the application level
-
10. Quality Requirements
This project is mainly focused on 4 basic quality attributes for an online game as GameY is. Those are good performance, high usability, easy maintainability and strong security.
10.1. Quality Tree
10.2. Quality Scenarios
| Attribute | Description | Metric |
|---|---|---|
Response time |
Time the system takes to react after a user’s abstraction |
The system should respond to user actions within 1 second |
Currency |
System’s capacity to support different games and users at the same time |
The system should support at least 25 simultaneous games or users without noticeable lag |
Intuitiveness |
Ease with which players can understand and play the game in a short period of time |
New players should be able to understand and play the game within 10 minutes. |
Continuity |
System’s capacity to keep games and sessions active, keeping their progress updated |
At least 90% of games and sessions should maintain their progress after interruptions. |
Modularization |
Organizational quality, where different functionalities of the program are located in different modules, so that changes in one part do not affect others |
The program should be organized in at least 3 independent modules to isolate changes |
API access |
Secure control to the game funcitionalities, avoiding non authorized uses of the game |
All unauthorized API requests should be blocked to prevent misuse |
User’s information |
Users' data protection, granting privacy and integrity. |
User critical data should be properly protected and encrypted |
11. Risks and Technical Debts
We identified some technical risks that can affect the development of the Y game project. These risks must be identified and registered, and we can mitigate or accept them.
11.1. Risks
Risks can appear like part of Quality Attributes scenarios. We categorized the next ones based on the risk criteria of the project.
| Area (Risk Criterion) | Brief Description | Mitigation measures | Probability (1-3) | Impact (1-3) | Total Risk |
|---|---|---|---|---|---|
Security |
The project needs to pass strict security tests. There is a risk to not complete these requirements when we implement the backend and expose vulnerabilities in the game or the connection. |
Do early validations, use security tools integrated in the Rust compiler (cargo audit) and make exhaustive code reviews. |
2 |
2 |
4 |
Data Integrity |
The system now uses three independent databases ( |
Implement compensating operations for cross-service data changes. Define clear ownership rules per data domain. Add integration tests that verify consistency across services after multi-step operations. |
2 |
3 |
6 |
Performance |
The game engine in Rust must allow to play against a bot with different strategies. If the decision algorithms of the robot are not optimized, the response time can degrade the experience of the user. |
Design efficient AI strategies, measure times of execution (benchmarking) in the Rust engine from the first sprints and profile the code if there is bottlenecks. |
2 |
2 |
4 |
Inter-service Communication Complexity |
The separation into three independent backend services (Auth Service, Users Service, Game Service) introduces inter-service HTTP calls for operations that previously were internal (e.g., JWT verification, linking statistics to user profiles). This adds latency, increases the number of potential failure points, and complicates local development and debugging. |
Define clear and versioned internal API contracts between services. Use Docker Compose to simplify local multi-service orchestration. Implement timeouts and fallback strategies for inter-service calls. Add end-to-end tests that cover cross-service flows. |
2 |
2 |
4 |
Unknowns |
Lack of deep knowledge in the ownership model of Rust and the typing of TypeScript. Sometimes we don’t have enough information about how an architecture can satisfy the requirements. |
Dedicate initial time to Proof of Concepts (PoC), read official documentation and establish pair programming to level the knowledge of the team. |
3 |
3 |
9 |
11.2. Technical debts
| Technical debt | Brief description |
|---|---|
External API Dependency |
Our app depends a lot of external services (like map services or data APIs). If those services change their API or go down, parts of our app will stop working. Right now, we don’t have a good abstraction layer to swap those services easily. |
Network Inconsistency |
The performance of the app depends directly on how good the user’s internet connection is. We still haven’t implemented a solid offline mode or cache system (like Service Workers), so with a bad connection the user experience will be very bad. |
Monolithic Configuration |
Because we don’t have much experience with server deployment, our first configuration will probably be a bit hardcoded or less modular than it should be. Later we need to refactor this to move to a more scalable setup using environment variables. |
Cross-service Data Consistency |
Operations that affect multiple data domains (e.g., user deletion, which must remove records from |
12. Glossary
| Term | Definition |
|---|---|
API Gateway |
Architectural component that serves as the single public entry point, routing client requests to backend services and managing cross-cutting concerns |
Nginx |
Web server and reverse proxy used to implement the API Gateway pattern in this system |
Anonymous Play |
Game mode where unregistered users can play without persisting their statistics or match history |
Arc42 |
Architecture documentation template followed in this project for structural consistency |
BFF (Backend for Frontend) |
Architectural pattern where the Web App Server orchestrates calls to multiple backend services (Users Service and Game Server) |
Barycentric Coordinates |
Coordinate system (x, y, z) used to represent positions on the hexagonal game board |
Board |
Hexagonal grid representing the Y game playing surface |
Bot Client |
External automated agent that plays through the system’s Bot API |
CORS (Cross-Origin Resource Sharing) |
HTTP-header based mechanism that allows a server to indicate which origins are permitted to access its resources from a browser |
Docker |
Containerization platform used for packaging and deploying all system services |
Game Server |
Rust-based microservice responsible for core game logic, move validation, and bot AI strategies |
Match |
Game session between two players (human or bot) with a specific board size and strategy configuration |
Microservices |
Distributed architecture pattern where the system is decomposed into independent services (Frontend, Web App Server, Users Service, Game Server) |
Move/Movement |
Player action placing a piece at specific coordinates on the board |
PlantUML |
Tool used for generating architectural diagrams throughout the documentation |
Registered Player |
User with an account who can access match history, statistics, and personalized features |
Reverse Proxy |
Server (Nginx) that forwards client requests to appropriate backend services and returns responses |
SQLite |
Embedded relational database used for persisting users, matches, and statistics |
Stateless Service |
Service that does not maintain persistent state between requests (e.g., Game Server) |
Strategy |
AI difficulty level and bot behavior configuration (e.g., random, heuristic) |
Upstream |
Nginx configuration directive defining a group of backend servers that can handle requests (e.g., users_backend, gamey_backend) |
Users Service |
Node.js microservice that acts as the single point of access to the database, managing authentication, user accounts, match persistence, and statistics |
WAL (Write-Ahead Logging) |
SQLite mode that enables concurrent reads during writes, improving database performance |
Web App Server |
Node.js/Express/TypeScript service implementing the BFF pattern, orchestrating requests between Frontend, Users Service, and Game Server |
Win Checker |
Algorithm that detects Y-shaped winning connections on the board to determine game outcome |
YBot |
Rust trait/interface defining the contract for bot strategy implementations |
YEN (Y-Encoded Notation) |
Domain-specific format for representing board positions and moves in the Y game, used for inter-service communication |
