Architectural decisions #79
DavidPedregal
started this conversation in
General
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
Note
Decisions are organized in categories and sorted in chronological order top-down.
Framework Decisions 📝
Decision: Reject Spring for Back-End
Status: Rejected ❌
Context: Spring was initially considered due to the team's Java expertise. However, its complexity in deployment and integration with GitHub Actions posed challenges compared to lighter alternatives like Node.js.
Decision: Reject Spring in favor of Node.js with Express. Spring’s overhead outweighed its benefits for this project’s scope and deployment needs.
Consequences: Avoided potential deployment bottlenecks but required shifting to a JavaScript-based stack, which may involve a learning curve for some team members.
Decision: Use Node.js with Express for Back-End
Status: Accepted ✅
Context: The team needed a robust and scalable framework for the back-end. Spring was considered due to the team's familiarity with Java, but it was deemed to complicate deployment and GitHub Actions workflows. Node.js with Express was evaluated as a lightweight, flexible alternative that simplifies development and deployment processes.
Decision: Use Node.js with Express for the back-end. This choice leverages JavaScript's ecosystem, aligns with modern deployment practices, and supports efficient GitHub Actions integration.
Consequences: Simplified deployment and CI/CD processes, but requires team members to adapt to JavaScript if not already proficient. Potential for faster development cycles due to Express's minimalistic approach.
Decision: Reject Create React App for Front-End
Status: Rejected ❌
Context: Create React App was part of the initial project skeleton but was found to be outdated with known vulnerabilities, making it unsuitable for a production-grade application. Alternatives like Next.js offered modern features and better security.
Decision: Reject Create React App in favor of Next.js. This ensures a secure, maintainable front-end with advanced rendering capabilities.
Consequences: Avoided security risks and maintenance issues but required refactoring the initial setup to align with Next.js.
Decision: Use React with Next.js for Front-End
Status: Accepted ✅
Context: The front-end required a modern, flexible framework. Create React App was rejected due to its obsolescence and vulnerabilities. Next.js was considered for its support for server-side rendering, static generation, and client-side rendering, offering flexibility to meet diverse application needs.
Decision: Use React with Next.js for the front-end. Next.js provides a robust framework for building performant, scalable applications with built-in optimizations.
Consequences: Improved performance and SEO capabilities due to server-side rendering. Increased complexity in setup and learning curve for team members unfamiliar with Next.js.
Front-End Decisions 📝
Decision: Use React as Main Front-End Technology
Status: Accepted ✅
Context: The project required a dynamic, component-based front-end framework. React was chosen for its popularity, ecosystem, and the team’s familiarity, ensuring rapid development and maintainability.
Decision: Use React as the primary front-end technology. Its component-based architecture supports reusable UI elements and aligns with Next.js.
Consequences: Faster development due to reusable components, but requires careful state management to avoid complexity in larger applications.
Decision: Use Material UI for Interface Components
Status: Accepted ✅
Context: The front-end needed a consistent, visually appealing design. Material UI was selected for its pre-built, customizable components that adhere to modern design principles, reducing development time for UI elements.
Decision: Use Material UI to enhance the visual appearance of the interface. It provides a professional look and feel with minimal effort.
Consequences: Improved UI consistency and development speed, but introduces a dependency that requires updates to stay aligned with Material UI’s versioning.
Testing and Code Quality Decisions 📝
Decision: Define Repository Naming Conventions
Status: Accepted ✅
Context: Consistent naming for commits, issues, and pull requests was needed to maintain repository clarity. Conventions were established to streamline collaboration and tracking.
Decision: Define and enforce conventions for commit, issue, and pull request messages as outlined in the repository’s naming conventions discussion.
Consequences: Improved repository organization and traceability, but requires team discipline to adhere to conventions.
Decision: Use Jest for Unit Testing
Status: Accepted ✅
Context: Unit testing was critical for ensuring code reliability. Jest was chosen for its simplicity, robust feature set, and compatibility with JavaScript projects, specifically version 29.7.0 for stability.
Decision: Implement unit tests using Jest (version 29.7.0). It supports the project’s testing needs with minimal configuration.
Consequences: Enhanced code reliability and easier debugging, but requires team members to learn Jest’s syntax and conventions.
Decision: Use Gherkin for Acceptance Criteria
Status: Accepted ✅
Context: Clear acceptance criteria were needed for testing. Gherkin was selected for its human-readable syntax, enabling collaboration between technical and non-technical team members in defining test scenarios.
Decision: Use Gherkin to define acceptance criteria for tests. It ensures clarity and alignment in testing requirements.
Consequences: Improved collaboration and test clarity, but requires training for team members unfamiliar with Gherkin’s syntax.
Decision: Use Gatling for Load Testing
Status: Accepted ✅
Context: The project required a robust tool to evaluate the application’s performance under high user loads. Gatling was selected for its ability to simulate thousands of concurrent users, its Scala-based DSL for readable test scripts, and its detailed performance metrics, which support identifying bottlenecks. Alternatives like JMeter were considered but found less intuitive for scripting and reporting.
Decision: Use Gatling for load testing. It provides a scalable, developer-friendly approach to performance testing with comprehensive reporting capabilities.
Consequences: Improved ability to detect performance issues early, but requires team members to learn Gatling’s Scala-based syntax and setup, which may involve a learning curve.
Session Management and Security Decisions 📝
Decision: Use JSON Web Token for Session Management
Status: Accepted ✅
Context: Secure and scalable session management was required. JWT was chosen for its stateless nature, enabling secure user authentication and authorization across services.
Decision: Use JWT for managing user sessions. It provides a secure, industry-standard approach to session handling.
Consequences: Simplified session management in distributed systems, but requires careful handling of token expiration and security practices.
Decision: Define Strategy for Game Information Storage
Status: Accepted ✅
Context: The application needed a method to store and retrieve game information tied to user sessions. A strategy leveraging session tokens was proposed to ensure data consistency and security.
Decision: Implement a strategy for storing and retrieving game information using session tokens. This ensures secure and efficient data access.
Consequences: Enhanced data security and user experience, but requires robust token management to prevent data access issues.
Decision: Use a Web Application Firewall (WAF) on the Server
Status: Accepted ✅
Context: The application required enhanced security to protect against common web-based attacks such as SQL injection, cross-site scripting (XSS), and DDoS. A Web Application Firewall (WAF) was considered to provide an additional layer of defense by filtering and monitoring HTTP traffic. Alternatives like manual security configurations were deemed insufficient for comprehensive protection and scalability.
Decision: Implement a WAF configured on the server. This ensures robust protection against web vulnerabilities and aligns with industry-standard security practices.
Consequences: Improved security posture and reduced risk of attacks, but introduces additional configuration complexity and potential performance overhead if not optimized. Requires periodic updates to WAF rules to address emerging threats.
Decision: Implement Optional Two-Factor Authentication (2FA) for Registered Users
Status: Accepted ✅
Context: To enhance user account security, the team considered implementing two-factor authentication (2FA) for registered users. Making 2FA optional was proposed to balance security with user convenience, allowing users who prioritize security to enable it without forcing all users to adopt it. Alternatives like mandatory 2FA were rejected to avoid alienating users who prefer simpler login processes.
Decision: Implement optional 2FA for registered users who choose to enable it. This provides an additional security layer for sensitive accounts while maintaining flexibility for users.
Consequences: Increased account security for users who opt in, but requires additional development effort to integrate 2FA mechanisms (e.g., authenticator apps or SMS). Users who opt out remain vulnerable to password-based attacks.
Decision: Store Previous Quiz Data in localStorage for "Guest Users"
Status: Accepted ✅
Context: The application needed a mechanism to retain quiz data for unregistered users to improve user experience by allowing them to resume or save their progress later. Storing data in the browser’s localStorage was proposed as a lightweight solution that avoids server-side storage for unauthenticated users. Alternatives like cookies were considered but rejected due to size limitations and privacy concerns.
Decision: Use localStorage to store previous quiz data for unregistered users, with an option to save to the server upon registration. This ensures a seamless experience for casual users while encouraging registration for persistent storage.
Consequences: Enhanced user experience for unregistered users and reduced server load, but localStorage data is browser-specific and may be cleared by users, potentially leading to data loss. Requires clear user communication about data persistence limitations.
Infrastructure and Deployment Decisions 📝
Decision: Use Docker for Microservices
Status: Accepted ✅
Context: The project required a containerized approach for microservices to ensure portability and scalability. Docker was chosen for its widespread adoption and ease of use in managing containers.
Decision: Use Docker for microservices and container management. It supports consistent environments across development and production.
Consequences: Improved deployment consistency and scalability, but introduces complexity in managing Docker configurations.
Decision: Use Oracle Cloud for Deployment
Status: Accepted ✅
Context: A reliable server was needed for continuous integration and deployment. A team member provided access to an Oracle Cloud server, which was deemed suitable for the project’s needs.
Decision: Use an Oracle Cloud server for integration and deployment. It provides a cost-effective, scalable hosting solution.
Consequences: Reduced hosting costs and simplified CI/CD, but dependency on a single team member’s server may pose risks if access is disrupted.
Decision: Implement Container Volume for Image Storage
Status: Accepted ✅
Context: Images downloaded from WikiData during question collection needed persistent storage. A container volume was proposed to ensure data availability across container restarts.
Decision: Implement a container volume for storing images from WikiData. This ensures data persistence and accessibility.
Consequences: Reliable image storage, but requires monitoring to manage storage growth and performance.
Decision: Reinstall Web Application Dependencies
Status: Accepted ✅
Context: Dependency management issues necessitated a clean slate for the web application. Removing and reinstalling dependencies at a specific time was proposed to resolve inconsistencies.
Decision: Remove and reinstall all web application dependencies at a designated time. This ensures a consistent dependency environment.
Consequences: Resolved dependency conflicts, but may cause temporary downtime during reinstallation.
Architecture and Code Organization Decisions 📝
Decision: Adopt Router-Based Architecture
Status: Accepted ✅
Context: Code organization needed to support scalability and maintainability. A router-based approach using separate .js files and [name]-server.js for routing was proposed to delegate functionality effectively.
Decision: Adopt a router-based architecture for code organization. This improves modularity and maintainability.
Consequences: Cleaner codebase and easier maintenance, but requires consistent naming and structure adherence.
Decision: Use Pino for Logging
Status: Accepted ✅
Context: Logging was needed for debugging and monitoring. Pino was chosen for its performance and simplicity, aligning with the project’s JavaScript stack.
Decision: Use Pino for logging across the application. It provides fast, structured logging.
Consequences: Improved debugging and monitoring, but requires configuration to optimize log storage and retrieval.
AI Management and Data Models Decisions 📝
Decision: Use Groq API for AI Model
Status: Rejected ❌
Context: The team needs a high-performance AI model to power the hint chat feature. The Groq API was evaluated for its ability to provide a decoupled interface, allowing seamless switching between various LLM models while maintaining fast processing and scalability. This flexibility was appealing to avoid vendor lock-in and support future model experimentation.
Decision: Use the Groq API to power the hint chat’s AI model. Its decoupled architecture and performance were expected to enable rapid development, easy model switching, and efficient real-time interactions.
Consequences: Facilitated quick prototyping and flexibility in testing different LLMs, but later evaluations showed that Groq’s general-purpose approach lacked the empathetic tone and contextual accuracy needed for the project’s user-centric focus. This led to a later decision to adopt Empathy LLM, requiring additional integration efforts.
Decision: Mitigate AI Model Hallucinations
Status: Accepted ✅
Context: The hint chat’s AI model was prone to hallucinations, impacting user experience. A strategy was needed to ensure reliable outputs.
Decision: Implement a strategy to mitigate AI model hallucinations in the hint chat by filtering the words that the LLM might contain in the response. This improves response accuracy without revealing the correct quiz answer.
Consequences: Enhanced AI reliability, but it require additional computational resources for "fine-tuning" the response.
Decision: Reject Groq API, Use Empathy LLM
Status: Accepted ✅
Context: The team evaluated AI models for the hint chat. The Groq API was considered but rejected in favor of the Empathy LLM, which better met the project’s needs for empathy and accuracy.
Decision: Reject the Groq API and use the Empathy LLM model. This aligns with the project’s focus on user-centric AI interactions and reduces some errors related with the previous implementation.
Consequences: Improved AI performance for specific use cases, but requires more coupling/integration with Empathy LLM’s ecosystem.
Decision: Define MongoDB Data Model for Questions
Status: Accepted ✅
Context: The application required a structured approach to collect, store, and model questions in MongoDB. A clear data model was needed to ensure efficient querying and storage.
Decision: Establish a data model for collecting, storing, and modeling questions in MongoDB. This ensures scalability and performance.
Consequences: Improved database performance and query efficiency, but requires careful schema design to avoid future refactoring.
Wihoot Component Architectural Decisions 📝
Decision: Use WebSockets for Real-Time Communication in Wihoot
Status: Accepted ✅
Context: The Wihoot multiplayer game required real-time communication to handle events such as player joins, chat messages, question navigation, and session updates. WebSockets, implemented via Socket.IO, were evaluated for their low-latency, bidirectional communication capabilities, ideal for synchronizing game state across clients. Alternatives like REST polling were considered but rejected due to high latency and server load under concurrent user scenarios.
Decision: Implement WebSockets using Socket.IO for Wihoot’s real-time communication. This ensures seamless, low-latency event handling for multiplayer interactions, such as joining sessions, submitting answers, and broadcasting game state changes.
Consequences: Improved user experience with real-time updates and efficient server-client communication. However, it introduces complexity in managing socket connections and handling disconnections, requiring robust error handling and session cleanup mechanisms.
Decision: Use Unique Codes for Joining Wihoot Sessions
Status: Accepted ✅
Context: To enable users to join specific Wihoot game sessions, a mechanism was needed to identify and route players to the correct session. Generating unique 6-character codes was proposed to simplify the joining process, allowing users to enter a code to connect via WebSockets. Alternatives like session IDs or URLs were considered but deemed less user-friendly or overly complex for casual players.
Decision: Use unique, randomly generated 6-character codes to allow players to join Wihoot sessions. The codes are validated server-side to ensure uniqueness and route players to the correct Socket.IO room.
Consequences: Simplified user experience for joining sessions and reduced the risk of unauthorized access through code validation. However, requires careful management to avoid code collisions and ensure codes remain unique in the MongoDB database.
Decision: Allow Unregistered Users to Play Wihoot
Status: Rejected ❌
Context: Early in development, the team explored allowing unregistered users to join Wihoot sessions to broaden the game’s appeal and reduce barriers to entry. This approach required handling unauthenticated socket connections and temporary player identifiers, which led to complexities in endpoint authentication, session persistence, and reliable communication with the server. These issues outweighed the benefits of open access, especially given the authentication requirements of certain endpoints.
Decision: Reject allowing unregistered users to play Wihoot in favor of restricting gameplay to registered users. The technical challenges and security risks of supporting unauthenticated users were deemed too significant.
Consequences: Avoided complexities in socket communication and authentication logic but limited the game’s accessibility to only registered users, potentially reducing the player base. This decision led to the adoption of a registered-users-only approach.
Decision: Restrict Wihoot to Registered Users Only
Status: Accepted ✅
Context: Initially, the team considered allowing both registered and unregistered users to participate in Wihoot games to maximize accessibility. However, supporting unregistered users introduced significant challenges, including issues with socket communication for unauthenticated clients, endpoint authentication conflicts, and difficulties in tracking player state reliably. Restricting Wihoot to registered users was proposed to streamline authentication and ensure consistent session management.
Decision: Restrict Wihoot gameplay to registered users only. This ensures all players are authenticated, simplifying socket communication and endpoint security while maintaining reliable session tracking.
Consequences: Improved security, simplified server-side logic, and enhanced reliability of socket-based communication. However, it may reduce accessibility for casual users who prefer not to register, potentially impacting user adoption.
Beta Was this translation helpful? Give feedback.
All reactions