Admin server
1️⃣ Présentation
Le coeur de l'application contenant la logique métier se situe dans le service : admin-server. Ce service NodeJS, écrit en Typescript avec le framework Express permet au dashboard de récupérer toutes les informations liées à son état via une API en HTTP (documenté via un Swagger). Il est stateless (cf refonte CLEAN) et stocke son état dans un Redis.
2️⃣ Traitements réguliers
Pour permettre à l'application de fonctionner différents traitements se font à intervalles réguliers :
-
pullEvents : ⌛/ 5s
Se charge de récupérer les évènements ces joueurs chez les différents providers.
-
computeScore : ⌛/ 1min
Calcule le score des joueurs dans le cadre d'un Gameday pour chaque ressource.
-
rulesEngine: ⌛ / 5s
Vérifie si dans le cadre d'un Recrutement un joueur a atteint les checkpoints de son scénario.
-
teamsEngine: ⌛ / 3s
Envoie une requête aux endpoints des équipes du Gameday pour vérifier la disponibilité de leur infrastructure.
-
garbageCollector: ⌛ / 1 min
Supprime et nettoie les comptes inutilisés.
3️⃣ Organisation du projet
Arborescence
├── src │ ├── /accounts // Gestion des comptes (pool de comptes, initialisation etc.) │ ├── /aws // Outillage lié à AWS (events, sdk etc.) │ ├── /cache // Connection à Redis qui sert de BDD │ ├── /chaos // Mise en place des chaos (destruction EC2 etc.) │ ├── /core // Types utiles dans toute l'application │ ├── /events // Récupération des events des providers │ ├── /gameday_config // Gestion de la configuration d'un Gameday │ ├── /garbage_collector // Nettoyage des comptes à intervalles réguliers │ ├── /gitlab_api // Outillage pour récupérer les scénarios sur Gitlab │ ├── /helpers // Fonctions utiles (calcul dates etc.) │ ├── /middlewares // Middlewares HTTP notamment pour l'authentification │ ├── /nuke // Outils de nettoyage des comptes │ ├── /rules_engine // Moteur de règles pour le Recrutement │ ├── /scenarios // Récupération et mise à disposition des scénarios │ ├── /scores // Calcul du score par ressource │ ├── /state // WARN : dossier historique de la variable State (cf refonte CLEAN) │ └── /teams // Gestion des équipes ├── tests // Tests unitaires de l'application │ ├── app.ts // Point d'entrée de l'application └── server.ts // Ensemble des controlleurs HTTP
Types
Le projet se concentre majoritairement autour des objets de type Team
(cf src/teams/domain/team_types.ts
) qui stockent toutes les informations liées à une équipe que ce soit dans le cadre d'un Gameday ou d'un Recrutement. Il possède des attributs stockant tous les scores, leurs events etc. et sont persistés dans le redis avec pour clef leur accountId.
4️⃣ Architecture CLEAN
Cette refonte est toujours en cours et même si elle est bien avancée de nombreux dossiers restent encore à refactorer. Elle se veut modulaire donc les dossiers peuvent être modifiés un à un sans impacter le reste de l'application. La liste des dossiers déjà modifiés se trouve dans le fichier .dependency-cruiser.js
dans la constante REFACTORED_FOLDERS
.
Problématique
Un travail autour de l'architecture du projet a été entrepris début 2022 pour permettre une meilleure stabilité et maintenabilité de l'application. En effet, plusieurs problèmes se posaient :
Etat stateful :
L'application était jusqu'alors dans un état un peu hybride avec l'état du jeu stocké dans un singleton (variable state
) partagé à travers toute l'application.
Dépendances et couplage :
L'application ayant été construite au fur et à mesure en ajoutant des briques de fonctionnalité, peu de travail a été fait pour penser à l'architecture global du projet et ainsi éviter un couplage trop fort. Ce couplage entrainait de nombreux problèmes lors de la modification d'un bout de code qui entrainait donc une avalanche d'autres modifications.
Généricité :
Actuellement, l'application était uniquement prévue pour des scénarios avec AWS mais le besoin d'ouvrir l'application à d'autres providers posait de nombreux problèmes à cause du couplage lié à l'infrastructure AWS.
Choix
Le choix a donc été fait de faire évoluer l'architecture de l'application pour se rapprocher de l'architecture CLEAN. L'idée étant de réduire le couplage au sein du projet et de passer l'application entièrement en stateless en se débarrassant de la variable state
.
Explication
L'idée principal étant de définir des couches d'abstraction bien distinctes imbriquées les unes dans les autres. Chaque couche ayant seulement le droit de dépendre de leur couche inférieur. Les couches le plus au centre représentant la logique métier de manière indépendante des frameworks et autres préoccupations d'infrastructure (BDD etc.) étant sur l'extérieur.
Architecture d'un dossier
Pour mieux se représenter notre implémentation de cette architecture voici un exemple avec le module scores :
├── /scores │ ├── /domain // Définition des différentes entités du domaine métier │ │ ├── resource.ts │ │ └── scores_types.ts │ ├── /infrastructure // Implémentation des services spécifiques à l'infrastructure (AWS, Azure, Redis) │ │ ├── /aws │ │ │ ├── ec2_resource.ts │ │ │ ├── loadbalancer_resource.ts │ │ │ └── rds_resource.ts │ │ └── /azure │ ├── /presentation // Couche de présentation (controllers HTTP etc.) │ │ └── scores_controller.ts │ ├── /useCase // Logique métier utilisant uniquement les classes du domaine │ │ └── score_use_case.ts │ └── index.ts // Fichier exportant les différents objets du module utiles pour l'extérieur
Pour vérifier l'avancement et la non-régréssion de cette évolution un outil de vérification de dépendances a été mis en place : depcruiser. Ces tests d'architecture sont lancables avec la commande npm run test:archi
et sont intégrés à la CI.