Files
Ustagammon/docs/adr/0007-architecture-et-conventions-de-code.md
2026-11-03 20:55:00 +01:00

5.3 KiB

0007. Architecture et conventions de code

  • Statut : accepté
  • Date : 2026-11-02
  • Décideurs : Pierre (stockage des parties, nommage, garde-fous), sur proposition de Claude Code

Contexte

Le code va être écrit par des agents et Pierre ne le relit pas en détail. Il doit donc rester simple, lisible par un humain, et protégé par des tests contre les dérives. Le cadrage impose déjà que le moteur soit caché derrière une interface, et demande l'annulation et les branches de partie (« Rejouer d'ici »).

Décision

1. La simplicité d'abord. Le code se lit sans connaître de patron de conception. Pas d'abstraction sans un deuxième usage réel, pas de framework sans besoin avéré, pas de couche « au cas où ». Dans le doute, la solution la plus directe.

2. Trois parties, une seule règle de dépendance.

  • Le domaine : les règles de l'application (partie, match, branches, coach, analyse, parcours). Du code pur : ni écran, ni stockage, ni appel direct au moteur. Il décrit l'interface Engine dont il a besoin (coups légaux, classement, videau, évaluation).
  • Le moteur : gnubg en C derrière engine/include/engine.h, et un adaptateur par plateforme (WebAssembly dans un Web Worker, ou JNI sur Android) qui implémente l'interface Engine du domaine.
  • L'application : écrans, stockage, presse-papiers, assemblage.

Les dépendances vont vers le domaine, jamais l'inverse. Aucun écran n'appelle le moteur directement. Ces règles sont vérifiées par des tests ; l'outil qui les vérifie sera choisi avec le socle (PWA ou Android) et signalé avec sa licence.

3. Une partie est un journal d'événements. Une partie est enregistrée comme la suite de ce qui s'est passé (GameStarted, DiceRolled, MovePlayed, CubeOffered, CubeTaken, CubeDropped, GameEnded…), sans jamais modifier un événement passé. Chaque événement pointe vers le précédent : une branche n'est qu'un nouvel événement accroché à un ancien. L'état de la partie se recalcule en rejouant les événements ; annuler revient à repartir de l'événement précédent ; l'export SGF est un parcours de l'arbre. Chaque événement porte un numéro de version de format. Le reste de l'application (réglages, progression, leçons) est stocké simplement, sans journal.

4. Le hasard est injecté. Le domaine reçoit sa source de dés : les tests sont reproductibles, et l'historique des dés d'une partie est consultable.

5. Nommage. Tous les identifiants du code sont en anglais (fichiers, types, fonctions, variables, clés JSON). Commentaires, documentation et messages de commit sont en français. Les textes de l'interface sont en français, dans des fichiers de textes séparés du code. En conséquence, l'interface C prévue sous le nom moteur.h (ADR 0005) s'appelle engine.h, avec le préfixe engine_.

6. Le vocabulaire du jeu, pas la cérémonie. On reprend du DDD le langage commun (un glossaire des termes du jeu, les mêmes dans le code, la doc et l'interface) et le découpage par sous-domaines en dossiers (game, analysis, learning, prompts). On n'en reprend pas les patrons tactiques (dépôts, fabriques, objets-valeurs systématiques) tant qu'un besoin concret ne les justifie pas.

7. Tests.

  • Domaine : tests unitaires rapides, avec un faux moteur.
  • Moteur : une même suite de tests de contrat, rejouée sur chaque adaptateur (ligne de commande, WebAssembly, Android), contre les positions de référence de gnubg de bureau.
  • Bout en bout : quelques scénarios seulement.

8. Notre code C (hors copie de gnubg) : C11, compilé avec -Wall -Wextra -Werror, sans état global ajouté par nous.

Alternatives écartées

  • Event sourcing pour toute l'application, avec projections (CQRS) : très traçable, mais lourd pour une application hors ligne à un seul utilisateur.
  • État classique pour les parties (position courante et historique à côté) : plus simple au départ, mais les branches et l'annulation deviennent du code au cas par cas.
  • Identifiants en français : cohérents avec la doc, mais écart avec les conventions habituelles du code et des bibliothèques.
  • Architecture hexagonale formelle, DDD complet : beaucoup d'interfaces et de vocabulaire pour une application de cette taille.
  • Règles d'architecture seulement documentées : sans relecture humaine du code, rien ne garantirait qu'elles sont respectées.

Conséquences

  • Le format des événements de partie est un contrat durable : le changer demande une migration et un ADR.
  • La structure des dossiers du domaine et l'outil de vérification des dépendances seront fixés avec le socle, à la fin de l'étape 0.
  • Les sous-agents reçoivent ces règles dans leurs consignes ; CLAUDE.md y renvoie.

Amendements

2026-11-03 — Le stockage des parties est un format durable

Avec les parties enregistrées (étape 1b), le schéma IndexedDB (base « ustagammon », version 1 : magasins games, journals, settings) et les clés localStorage de l'application (ustagammon.settings, ustagammon.last-board, et le repli des parties) rejoignent le journal d'événements parmi les formats durables : toute modification passe par une montée de version avec migration testée, jamais par une réécriture qui perdrait les parties des joueurs.