ADR — Architecture Decision Records
Un ADR (Architecture Decision Record) fige une décision technique structurante : son contexte, les options examinées, la décision retenue et ses conséquences. Sur Kohinos, un ADR est daté, irréversible (ou réversible avec un coût explicite), et engage tous les contributeurs suivants. Le format est emprunté à Michael Nygard : court, factuel, sans enrobage marketing.
Si tu hésites entre deux approches et que la décision sera durable (changer d'avis dans 6 mois coûterait une migration, une bdd, un train de tests, ou casserait un contrat externe) : écris un ADR. Si la décision est locale à une classe et triviale à inverser, un commentaire ou un commit suffit.
Format ADR Kohinos
Inspiré de Joel Parker Henderson. Chaque ADR vit dans
docs/adr/NNNN-titre-kebab.md, numéro sur 4 chiffres
(ADR-0001, ADR-0002…). Structure :
| Section | Contenu attendu |
|---|---|
Title | Verbe + sujet, à l'impératif : « Utiliser libsodium pour les signatures ». |
Date | YYYY-MM-DD du jour de décision (pas d'écriture rétrospective sans le mentionner). |
Status | Proposed · Accepted · Deprecated · Superseded by ADR-XXXX. |
Deciders | Personnes ou rôles ayant tranché (ex. : trésorier, dev solo, mainteneur). |
Context | Pourquoi le problème se pose maintenant. Contraintes (techniques, légales, économiques). |
Decision | Une phrase à l'impératif. Pas de « on pourrait ». |
Consequences | Positives, négatives, neutres. Ce que cette décision ferme. |
Alternatives | Options écartées + raison du rejet (1 ligne par option). |
Related | Liens vers ADRs voisins, issues, PR, RFC. |
Liste des ADRs Kohinos
Les ADRs ci-dessous sont soit déjà rédigés dans docs/adr/, soit
implicites (figés dans .claude/rules/global.md, CLAUDE.md,
CHANGELOG.md) et en attente de formalisation. Le statut indique l'état actuel.
Chaque titre ci-dessous est cliquable vers une page détaillée.
ADR-0001 — AGPL-3.0 non négociable
- Statut : Accepted · Date : origine du projet (2014).
- Motivation : garantir que toute amélioration faite par un tiers hébergeant Kohinos reste disponible à la communauté MLCC. Une licence permissive ouvrirait la porte à un fork SaaS propriétaire.
- Conséquence visible : en-tête
LICENSE, refus de dépendre d'une lib non-compatible AGPL, refus des forks closed-source.
ADR-0002 — Pas de SaaS cloud dans le path critique (souveraineté)
- Statut : Accepted · Date : 2024-Q4.
- Motivation : Kohinos est une infrastructure monétaire communautaire pour Fédération Kohinos. Une panne ou un changement de tarif d'un SaaS propriétaire ne doit pas pouvoir interrompre les prélèvements, les redistributions, ni exfiltrer les données adhérents.
- Conséquence visible : Monolog (pas Sentry cloud),
symfony/mailerSMTP self-host (pas SendGrid), Renovate (pas Dependabot), GrumPHP (pas SaaS pre-commit). Voir.claude/rules/mcp-policy.md.
ADR-0003 — libsodium pour toute signature financière
- Statut : Accepted · Date : 2025-Q3.
- Motivation : les primitives
openssl_*imposent des choix de courbes / paddings dangereux. libsodium expose des APIs « misuse-resistant » (Ed25519, Curve25519, XChaCha20-Poly1305) — alignées avec la roadmap Reticulum / LoRa offline. - Conséquence visible : aucune
openssl_sign()dans le domaine financier ; voir cryptographie. Exception encore tolérée : chiffrement IBAN historique (à migrer, cf. ADRs proposés).
ADR-0004 — Doctrine attributes uniquement (pas d'annotations docblock)
- Statut : Accepted · Date : 2025-04 (migration SF7).
- Motivation :
config/packages/doctrine.yamlest entype: attribute. Les annotations docblock ne sont plus lues : slugsNULLsilencieux,postPersistignoré, comptes non créés. - Conséquence visible : tout nouveau code utilise
#[ORM\Column],#[Gedmo\Slug],#[ORM\HasLifecycleCallbacks]. Règle dure dansCLAUDE.mdet.claude/rules/global.md.
ADR-0005 — Twig + Bootstrap + Vanilla JS (pas de SPA)
- Statut : Accepted · Date : 2024-Q1 (audit frontend).
- Motivation : un SPA React/Vue/Angular ajoute une chaîne de build lourde, un risque d'éclatement front/back, et exclut les bénévoles non-frontistes. L'app reste rendue serveur ; le JS reste discret et progressif (Vanilla ES2020+, Web Components si besoin).
- Conséquence visible : stack
Twig + Bootstrap 5.2 + Webpack Encore. Pas de JS/CSS inline en Twig. Exceptions tolérées : peer deps Select2, CKEditor.
ADR-0006 — Auto-hébergement Raspberry Pi friendly (low-tech)
- Statut : Accepted · Date : 2025-Q2.
- Motivation : une MLCC doit pouvoir s'auto-héberger sur un Raspberry Pi 4/5 + SSD + onduleur. RAM < 4 Go, CPU ARM, IO disque limitées. Cohérent avec la priorité sobriété.
- Conséquence visible : pas de Kafka, pas d'Elasticsearch, pas de cluster Redis. Stack : MariaDB + Symfony Messenger (broker DB) + cron. Optimisations OPCache + APCu suffisent.
ADR-0007 — MariaDB en prod, SQLite in-memory en test
- Statut : Accepted · Date : 2025-Q2.
- Motivation : MariaDB en prod pour les vues, triggers,
PESSIMISTIC_WRITE, fulltext. SQLite in-memory en test pour des suites < 30 s sans dépendance externe. Les divergences SQL sont surveillées par CI. - Conséquence visible :
config/packages/test/doctrine.yamlroute sursqlite::memory:. Migrations Doctrine doivent rester portables ; les triggers MariaDB-only sont testés en intégration.
ADR-0008 — Symfony 7.2 → 7.4 LTS comme cible
- Statut : Accepted · Date : 2025-04 (post-migration SF4→SF7). Supersede de l'ancien ADR-0010 (SF6.4).
- Motivation : SF7.4 sortira en LTS fin 2026 ; on évite un saut majeur en restant sur la même branche. PHP 8.4 déjà supporté.
- Conséquence visible :
composer.jsoncontraint àsymfony/* ^7.2. CI teste sur PHP 8.2/8.3/8.4.SECURE_SCHEME=httpen test,createClient()appelé une seule fois par classe.
ADR-0009 — Doctrine ORM 2.14 / DBAL 3.x (pas DBAL 4.x)
- Statut : Accepted · Date : 2025-05.
- Motivation : DBAL 4 casse l'API des types custom (IBAN chiffrés, UUID binaires) et certains bundles Sonata ne sont pas encore compatibles. Bloque sur DBAL 3.x jusqu'à audit complet.
- Conséquence visible :
"doctrine/dbal": "^3.7"verrouillé danscomposer.json. Renovate ne propose pas le bump majeur. ADR à réviser une fois Sonata 5.x stabilisé.
ADR-0010 — Sonata Admin 4.x (post-migration SF7)
- Statut : Accepted · Date : 2025-04.
- Motivation : Sonata 4 est la dernière version compatible SF7.2 + DBAL 3. Sonata 5 reste à valider sur l'ensemble du back-office (Flux, SSA, Transferts).
- Conséquence visible :
$this->trans()remplacé partout par$this->getTranslator()->trans(...).DatagridMapperétantfinal, certains tests passent en integration (voir sonata-admin).
ADR-0011 — API Platform 3.x — auth par API key header (pas JWT)
- Statut : Accepted · Date : 2025-Q3.
- Motivation : JWT impose une rotation de clés, un revocation mechanism, et un cycle de refresh tokens — trop lourd pour une API consommée par quelques intégrations (HelloAsso, PayZen, exports). Les API keys sont rotables manuellement.
- Conséquence visible : header
X-API-KEYcontrôlé par un guard authenticator dédié ; pas de bundle JWT. Voir api.
ADR-0012 — Webhooks HelloAsso : idempotence via helloassoid
- Statut : Accepted · Date : 2025-08.
- Motivation : HelloAsso peut rejouer un webhook (timeout, retry exponentiel). Un double traitement crée de la monnaie. La clé d'idempotence est l'identifiant unique HelloAsso du paiement.
- Conséquence visible : colonne
helloassoidindexée unique sur la table de traçabilité ;SELECT ... FOR UPDATEavant l'écriture. Détails dans webhooks.
ADR-0013 — UMR SEPA format KOHINOS-YYYYMMDD-XXXXXXXX
- Statut : Accepted · Date : 2025-Q2.
- Motivation : l'Unique Mandate Reference doit être
stable, lisible humainement (audit trésorier), et collisionless. Le préfixe
KOHINOS-identifie l'émetteur ; la date évite les collisions cross-année ; 8 hex de l'UUID v4 donnent > 4 milliards de variantes/jour. - Conséquence visible :
SepaMandate::generateMandateReference()après double-flush (UUID dispo). Bloqué par contrainte unique en base.
ADR-0014 — SSA detection via flux.solidoume_parameter_id IS NOT NULL
- Statut : Accepted · Date : 2025-09.
- Motivation : historiquement, on identifiait un flux SSA
par sa
reference(préfixe « SSA… ») ou par sadiscr(table héritière). Les deux ont mené à des doublons et à des faux positifs après refactor i18n. La FKsolidoume_parameter_idest l'unique source de vérité. - Conséquence visible : tous les repos SSA filtrent sur
WHERE solidoume_parameter_id IS NOT NULL. Lareferencene sert plus qu'à l'affichage. Voir ssa.
ADR-0015 — Atomicité via PESSIMISTIC_WRITE + Operation atomique
- Statut : Accepted · Date : 2025-11 (lié à ADR-0001 — double-entry bookkeeping).
- Motivation : deux prélèvements concurrents ne doivent pas pouvoir lire le même solde et l'épuiser deux fois. L'optimistic locking Doctrine retry est trop coûteux en cron mensuel. Lock pessimiste avant calcul du solde.
- Conséquence visible : tout service touchant un compte fait
$em->lock($account, LockMode::PESSIMISTIC_WRITE). UneOperationest créée comme unité atomique. Détails : invariants.
ADR-0016 — MCP local-only
- Statut : Accepted · Date : 2026-Q1.
- Motivation : les Model Context Protocol servers cloud (GitHub MCP, Linear MCP, Slack MCP, Sentry MCP cloud) exfiltrent code et données adhérents vers des tiers. Refus de principe : tous les MCP doivent tourner sur la machine du dev ou sur un Raspberry Pi local.
- Conséquence visible : liste blanche dans
.claude/rules/mcp-policy.md:symfony-ai-mate,context7,mcp-server-mysqlread-only,code-review-graph.
ADR-0017 — Pas de mock DB pour les tests financiers
- Statut : Accepted · Date : 2025-Q3.
- Motivation : mocker l'
EntityManagerdans des tests financiers donne une illusion de couverture. Les bugs réels viennent des contraintes DB (uniques, FK, lock), pas de l'API ORM. SQLite in-memory tourne en < 30 s sans CI lourde. - Conséquence visible :
AbstractFunctionalTestCaseetAbstractServiceTestinstancient une vraie DB SQLite. Les mocks restent pour les externes (Mailer, API HelloAsso, API PayZen). Voir tests.
ADR-0018 — bcmath / brick/math pour le calcul monétaire
- Statut : Accepted · Date : 2025-Q3.
- Motivation :
floaten PHP perd des centimes sur des additions de plusieurs milliers (IEEE 754). Une MLCC ne peut pas se permettre0.1 + 0.2 = 0.30000000000000004.bcmathoubrick/mathgarantissent l'exactitude décimale. - Conséquence visible : tous les montants sont en
BigDecimal(chaîne) à travers les services ; en base, colonnesDECIMAL(15,2). PHPStan refusefloatsur un nom de variable$amount*ou$balance*.
ADRs proposés / à formaliser
Décisions actuellement implicites (dans les règles dures, CHANGELOG, ou commits) mais
qui méritent un ADR dédié. Statut Proposed.
- ADR-0019 — Migration IBAN
openssl_*→ libsodium : le chiffrement actuel des IBAN utilise encoreopenssl_encryptviaapp:migrate-iban-encryption. À migrer verssodium_crypto_secretboxavec rotation de clé maître. - ADR-0020 — Choix d'un broker Messenger (in-DB vs RabbitMQ local) :
tant que la charge reste faible, le transport
doctrine://suffit. À évaluer pour les rappels SSA d'avant clôture (peak mensuel). - ADR-0021 — Format canonique pour
operation.payloadpré-hash : le hash chain dépend d'une sérialisation déterministe (clés triées, pas d'espaces, encodage UTF-8). Décrit dans cryptographie mais pas formellement figé. - ADR-0022 — Politique de rotation des clés Ed25519 : fréquence, stockage de l'ancienne clé pour la vérification rétrospective, procédure de révocation.
- ADR-0023 — Stratégie d'archivage des Flux > 7 ans : conservation légale (10 ans pour la compta, 6 ans pour les pièces) vs sobriété (BDD < 5 Go sur Pi).
Comment ajouter un ADR
- Identifier le prochain numéro disponible dans
docs/adr/(ex. :0019). - Créer le fichier
docs/adr/0019-titre-en-kebab.mden copiant le template duREADME.mddu dossier. - Remplir toutes les sections — pas de
TODO, pas deTBD. Si tu ne peux pas remplir, ne crée pas encore l'ADR. - Mettre à jour le tableau récapitulatif dans
docs/adr/README.md. - Ajouter une ligne dans
docs/INDEX.mdsection ADR (avec pictogramme 🟢). - Mentionner l'ADR dans
CHANGELOG.md(section technique) — pas dansCHANGELOG_USER.md(les ADRs sont invisibles aux utilisateurs). - Lier l'ADR depuis chaque page technique impactée (ex. : ADR-0014 lié depuis ssa.html).
- Si l'ADR remplace un précédent, marquer l'ancien
Superseded by ADR-XXXXsans le supprimer (l'historique reste lisible).
ADRs superseded
- ADR pré-2025 « Symfony 4.4 » — superseded par
ADR-0008 (Symfony 7.2 → 7.4). La migration est achevée
(cf.
docs/migration/RAPPORT_MIGRATION_SF74.md). - Ancien ADR-0010 « Symfony 6.4 / PHP 8.1+ » — superseded par ADR-0008. Le code cible PHP 8.4 (min 8.2).
- Ancien « annotations docblock Doctrine » — superseded par
ADR-0004. Mapping
type: attributeobligatoire ; les anciens docblocks ne sont plus lus. - Ancien « SwiftMailer » — retiré, remplacé par
symfony/mailer. Pas d'ADR formel mais traçable dansCHANGELOG.md2025-04.
Lecture croisée
- cryptographie — concrétise ADR-0003, ADR-0015, ADR-0021, ADR-0022.
- invariants — vérifie ADR-0001, ADR-0014, ADR-0015, ADR-0018.
- api — applique ADR-0011, ADR-0002 (souveraineté).
- webhooks — applique ADR-0012.
- ssa — applique ADR-0014, ADR-0018.
- sonata-admin — applique ADR-0010.
- tests — applique ADR-0007, ADR-0017.
- conventions — décline ADR-0004, ADR-0005, ADR-0018.
Sources
docs/adr/README.md— sommaire officiel des ADRs versionnés.docs/adr/0001-double-entry-bookkeeping.md,0002-sepa-debit-day-constraints.md,0009-test-driven-development.md— exemples rédigés..claude/rules/global.md— refus automatiques = ADRs implicites..claude/rules/mcp-policy.md— concrétise ADR-0016.CHANGELOG.md— date et contexte des décisions techniques majeures.doc/ARCHITECTURE.md— vue d'ensemble, rappelle les patterns d'ADR.docs/migration/RAPPORT_MIGRATION_SF74.md— historique de la migration qui a généré ADR-0004, 0008, 0010.