-
Notifications
You must be signed in to change notification settings - Fork 0
Architecture Decisions Record
This section documents the most relevant architectural decisions made during the design of the system. Each decision includes context, alternatives, rationale, and consequences to ensure traceability and transparency.
Status: Accepted
The system consists of three main functional areas:
- User interface (Web application)
- User management
- Game engine logic
These areas could be implemented either as a monolithic application or as independent services.
The system is implemented as a set of loosely coupled services, each running independently and communicating via HTTP APIs.
- Monolithic architecture
- Service-oriented (multi-container) architecture
The service-oriented approach was selected because:
- It enforces clear separation of concerns
- It allows independent development and testing
- It aligns with container-based deployment
- It improves maintainability and scalability
Positive:
- Independent evolution of components
- Clear API contracts
- Better modularity
Negative:
- Increased deployment complexity
- Network communication overhead
Status: Accepted
The system needs to be executable in development environments and easily portable across machines.
All services are deployed using containerization and orchestrated together.
- Native execution on host machine
- Containerized execution
Containerization ensures:
- Environment consistency
- Dependency isolation
- Simplified onboarding
- Infrastructure reproducibility
Positive:
- Predictable deployments
- Technology independence per service
Negative:
- Slight overhead compared to native execution
Status: Accepted
The project requires a backend and persistence solution that enables rapid development, flexibility, and ease of deployment.
Technologies evaluated:
- MongoDB (document database)
- SQL (relational databases)
- Neo4j (graph database)
- Node.js vs Spring (backend frameworks)
Use:
- Node.js for backend services
- MongoDB as the primary database
- SQL databases
- Neo4j
- Spring (Java-based backend)
- Flexible, document-oriented schema
- Well-suited for semi-structured and evolving data
- Faster iteration during early development
- Natural JSON serialization
- Strong integration with JavaScript/TypeScript
- Unified language across frontend and backend (JavaScript/TypeScript)
- Reduced stack complexity
- Large ecosystem for web APIs
- Asynchronous model suitable for I/O-heavy applications
Positive:
- Faster development cycles
- Flexible data model
- Reduced integration complexity
- Consistent technology stack
Negative:
- Less strict relational integrity than SQL
- Complex queries may be harder than in relational systems
- Requires discipline for data consistency at application level
Status: Accepted
The system requires authentication that:
- Supports user registration and login
- Scales without server-side session storage
- Integrates with a REST API architecture
Alternatives evaluated:
- Traditional sessions (cookies + server storage)
- OAuth 2.0
- JWT (JSON Web Tokens)
Use JWT (JSON Web Tokens) for authentication and session management.
- OAuth 2.0
- Server-side sessions
- Stateless authentication
- Enables horizontal scalability
- Simple integration with REST APIs
- Low infrastructure overhead
- Designed for third-party authorization
- Adds unnecessary complexity for internal authentication
- Requires server-side storage
- Harder to scale horizontally
- Introduces tighter coupling
Positive:
- Lightweight and scalable authentication
- No session storage required
- Well-suited for modern frontend applications
Negative:
- Token revocation is more complex
- Requires careful handling of expiration and refresh tokens
- Security depends on proper client-side storage
Status: Accepted
To support real user management, persistent storage is required.
Introduce a dedicated database accessed exclusively through the Users Service.
- In-memory storage
- File-based storage
- Ensures durability and consistency
- Enables long-term user management
- Supports future features (authentication, statistics)
- Maintains separation of concerns
Positive:
- Persistent user data
- Clear data ownership
- Improved scalability
Negative:
- Additional infrastructure
- Requires data modeling and connection management
Status: Accepted
The system includes both user management and game execution logic.
The game engine is fully independent from user management and persistence.
- Enforces single responsibility principle
- Improves reusability of the game engine
- Prevents tight coupling between domains
Positive:
- Clear domain boundaries
- Easier testing
- Higher reusability
Negative:
- Requires coordination via APIs
Status: Accepted
Different components require different optimizations.
- Frontend: React + TypeScript + Vite
- User Service: Node.js (Express)
- Game Engine: Rust
- Single unified stack
- Fully compiled stack
- React enables fast UI development
- Node.js allows rapid API development
- Rust ensures performance and safety for game logic
Positive:
- Best tool for each component
- High performance where needed
- Improved developer productivity
Negative:
- Polyglot complexity
- Multiple build and deployment processes
Status: Accepted
Services need a communication mechanism.
Use RESTful HTTP APIs with JSON payloads.
- GraphQL
- Message queues
- Direct calls
- Simplicity and widespread adoption
- Easy debugging and testing
- Stateless communication
Positive:
- Standardized communication
- Easy integration
Negative:
- Potential inefficiencies vs GraphQL
- HTTP overhead
Status: Accepted
The system requires observability.
Use Prometheus for metrics and Grafana for visualization.
- No monitoring
- Commercial tools
- Open-source and cost-effective
- Strong ecosystem compatibility
Positive:
- Real-time system insights
- Improved reliability
- Educational value
Negative:
- Additional setup complexity
- Extra infrastructure components
Status: Accepted
The platform was originally exposed over plain HTTP. The deployment target is a single Ubuntu VM with Docker Compose, and the system needed HTTPS with minimal operational overhead and no changes to internal service code.
Use Caddy as the public-facing reverse proxy to terminate TLS and forward traffic to the internal gateway over the Docker network.
- TLS directly in the Node.js gateway: Couples TLS concerns to application code, complicates certificate renewal, and requires manual handling of ACME protocols. Discarded to preserve separation between edge security and business logic.
- Nginx + Certbot: Widely used and well-documented, but requires separate Certbot configuration, cron-based renewal, and more manual setup. Discarded in favour of a solution with automatic certificate lifecycle management out of the box.
- Traefik: Capable and Docker-native, but more complex to configure for a single-VM setup with no orchestrator. The operational overhead was not justified at this scale.
- Caddy: Automatic certificate provisioning and renewal via ACME, secure defaults, simple declarative configuration. Selected option.
Caddy was selected because it provides automatic certificate provisioning and renewal with minimal configuration, keeps TLS concerns separated from application logic, and is well-suited to a single-VM Docker Compose deployment. It reduces both the time to ship HTTPS and the ongoing operational burden.
Positive:
- Automated certificate lifecycle with no manual renewal
- Clean separation between edge security and application routing
- Faster HTTPS rollout with less custom code
Negative:
- One additional infrastructure component to operate
- Certificate issuance depends on ACME reachability and correct DNS resolution
Status: Accepted
The system is deployed on a VM where managing a custom domain adds cost and friction for an academic or prototyping environment. A stable public hostname is required for TLS certificate issuance via ACME.
Use a DuckDNS hostname as the public domain for the application.
- Custom paid domain with static IP: Provides full control and branding, but introduces registration cost and DNS management overhead. Not justified for the current deployment context.
- Raw IP access: No DNS dependency, but incompatible with ACME-based certificate issuance, which requires a resolvable domain name. Discarded.
- DuckDNS: Free, fast to set up, and fully compatible with ACME certificate workflows. Selected option.
DuckDNS was selected primarily because it is free, which is the decisive factor in an academic or prototyping context. It also integrates cleanly with ACME-based certificate issuance (used by Caddy in ADR-10) and eliminates the need for manual DNS edits when the VM's IP changes.
Positive:
- Zero cost
- Stable public hostname compatible with automated HTTPS provisioning
- No manual DNS edits required on IP changes
Negative:
- External dependency on DuckDNS availability
- No custom branding; not suitable for a production product
Status: Accepted
After enabling HTTPS via Caddy (ADR-10), plain HTTP access remained possible. Allowing both protocols in parallel increases the risk of insecure usage and inconsistent canonical URLs.
Enforce permanent 301 redirection from HTTP to HTTPS at the edge.
- Keep HTTP and HTTPS in parallel: Avoids redirect overhead but leaves an insecure entry point open. Discarded.
- Temporary redirect (302): Functionally equivalent in the short term but does not signal permanence to browsers or CDNs, undermining long-term caching and canonical URL behaviour. Discarded.
A permanent redirect is the standard approach to enforce encrypted transport, establish a single canonical URL scheme, and let browsers cache the redirect to reduce future latency. The configuration cost is trivial within Caddy.
Positive:
- All traffic encrypted in transit
- Consistent URL scheme across frontend and API
- Browsers cache the redirect, reducing future plaintext requests
Negative:
- Legacy clients expecting plain HTTP will fail without warning
- Reverting to mixed-protocol access requires an explicit configuration change
The architecture prioritizes:
- Modularity and separation of concerns
- Scalability through stateless services
- Developer productivity and rapid iteration
- Flexibility in both data modeling and technology choices
The combination of Node.js, MongoDB, JWT, and a service-oriented architecture provides an effective balance between simplicity, performance, and scalability for the system.