ajout patterns

This commit is contained in:
MaksTinyWorkshop
2026-03-12 17:16:05 +01:00
parent 39067b153a
commit 1ac757558b
6 changed files with 559 additions and 478 deletions

View File

@@ -8,27 +8,380 @@ Objectifs :
- éviter de reposer les mêmes questions
- assumer les compromis
Dernière mise à jour : 2026-03-10
Dernière mise à jour : 2026-03-12
---
## Index
## Pré-index par section
1. [Global](#section-global)
2. [Infra](#section-infra)
3. [Backend](#section-backend)
4. [n8n](#section-n8n)
## Index détaillé
### 1. Global
<a id="section-global"></a>
- [Story sizing — foundations vs qualité non bloquante](#decision-story-sizing-foundations)
- [Code review adversariale — contexte frais](#decision-code-review-adversariale)
- [Workflows n8n complexes = mini-systèmes](#decision-n8n-mini-systemes)
- [Le front-end est un logiciel en production](#decision-frontend-production)
- [Le back-end est un logiciel en production](#decision-backend-production)
- [Contrats dAPI explicites et versionnés](#decision-contrats-api)
- [Single source of truth des contrats — schémas runtime partagés (Zod) + z.infer (No-DTO)](#decision-contrats-sso-zod)
- [User views — User public par défaut + MeUser explicite](#decision-user-views)
### 2. Infra
<a id="section-infra"></a>
- [Structure Docker et données persistantes](#decision-structure-docker)
- [Accès réseau des VM de développement via Tailscale](#decision-reseau-tailscale-vm-dev)
### 3. Backend
<a id="section-backend"></a>
- [Le back-end est un logiciel en production](#decision-backend-production)
- [Contrats dAPI explicites et versionnés](#decision-contrats-api)
- [Gestion standard des erreurs et des statuts HTTP](#decision-erreurs-http)
- [Migrations et évolution de schéma maîtrisées](#decision-migrations)
- [Observabilité minimale obligatoire](#decision-observabilite)
- [Authentification et autorisation centrales](#decision-auth-central)
- [Idempotence et gestion des retries](#decision-idempotence-retries)
- [Structure Docker et données persistantes](#decision-structure-docker)
- [Accès réseau des VM de développement via Tailscale](#decision-reseau-tailscale-vm-dev)
### 4. n8n
<a id="section-n8n"></a>
- [Workflows n8n complexes = mini-systèmes](#decision-n8n-mini-systemes)
---
## _Format standard dune décision_
## <Titre>
- Date : YYYY-MM-DD
- Statut : Proposed | Accepted | Deprecated
- Périmètre : n8n | backend | infra | global
### Contexte
### Options envisagées
### Décision
### Justification
### Conséquences
---
## 1. Global
<a id="decision-story-sizing-foundations"></a>
## Story sizing — foundations bloquantes vs qualité non bloquante (CI mobile)
- Date : 2026-03-09
- Statut : Accepted
- Périmètre : global
### Contexte
Des items “infra” ont été mis dans des stories foundations depic alors quaucune story métier suivante nen dépendait (ex : CI Maestro mobile iOS/Android).
Résultat observé : story foundations “never-done”, friction et coût de contexte, sans bénéfice sur le throughput métier.
### Options envisagées
- Mettre toutes les améliorations de qualité dans foundations “par principe”
- Séparer les prérequis réellement bloquants du reste (qualité non bloquante)
### Décision
On distingue explicitement :
- **Prérequis bloquants** : à inclure dans foundations (les stories suivantes en dépendent)
- **Qualité non bloquante** : story indépendante, en parallèle ou après, sans bloquer le métier
### Justification
- Un epic doit pouvoir avancer sur le métier dès que les dépendances techniques minimales sont là
- Les chantiers “qualité” (CI mobile, perf, audits…) ont souvent une inertie qui ne doit pas geler lepic
### Conséquences
- Pour chaque AC “infra” en foundations : poser la question “la story X+1 est-elle bloquée si ce nest pas fait ?”
- Si la réponse est non : sortir lAC en story dédiée (tag qualité / infra), et la planifier à part
---
<a id="decision-code-review-adversariale"></a>
## Code review adversariale — passe dédiée en contexte frais
- Date : 2026-03-09
- Statut : Accepted
- Périmètre : global
### Contexte
Certaines issues CRITICAL sont invisibles dans le contexte dimplémentation (biais de confirmation : “je sais comment cest censé marcher”).
Elles émergent uniquement en lecture froide : fondations manquantes, usages dépréciés, invariants non respectés (ex : sessions sans TTL).
### Options envisagées
- Review “au fil de leau” dans le même contexte que limplémentation
- Review dédiée, séparée, en contexte frais (nouvelle session / nouveau modèle / reviewer différent)
### Décision
La code review doit être une passe **séparée** de limplémentation, en **contexte frais**, avec un objectif explicite : chercher des CRITICAL sans concession.
### Justification
- Les incohérences systémiques (sécurité, idempotence, TTL, drift de contracts) se détectent mieux en lecture froide
- Réduit fortement le coût de debug tardif (prod/staging) et les refactors “de fondations”
### Conséquences
- Process recommandé :
1. Dev termine limplémentation, marque la story `review`
2. Nouvelle session (contexte frais) : charger uniquement story + diff
3. Review adversariale : lister CRITICAL + mitigations
4. Corriger avant `done`
---
<a id="decision-frontend-production"></a>
## Le front-end est un logiciel en production
- Date : 2026-01-25
- Statut : Accepted
- Périmètre : global
### Contexte
Les applications front-end modernes (SPA, webapps) portent une part significative
de la logique applicative : state, validations, permissions, erreurs, performance,
sécurité côté client, expérience utilisateur.
Les traiter comme une simple “couche UI” conduit régulièrement à :
- une dette technique rapide,
- des bugs difficiles à diagnostiquer,
- une expérience utilisateur incohérente,
- une maintenance coûteuse dans le temps.
### Options envisagées
- Traiter le front-end comme une couche de rendu légère, peu structurée
- Traiter le front-end comme un logiciel à part entière, avec des exigences similaires au backend
### Décision
Le front-end est traité comme un **logiciel en production**.
Il est soumis aux mêmes principes que le backend :
- architecture explicite,
- gestion des erreurs,
- conventions de code,
- tests au bon niveau,
- attention portée à la maintenabilité et à lévolution.
### Justification
- Le front concentre une logique métier et technique réelle
- Les bugs front ont un impact direct sur les utilisateurs
- La complexité augmente mécaniquement avec le produit
- Les choix initiaux conditionnent fortement la maintenabilité future
### Conséquences
- Mise en place de patterns front validés et documentés
- Gestion explicite des états UI, erreurs et formulaires
- Attention portée à la performance et à laccessibilité
- Acceptation dun léger surcoût initial pour réduire la dette long terme
---
<a id="decision-contrats-sso-zod"></a>
## Single source of truth des contrats — schémas runtime partagés (Zod) + z.infer (No-DTO)
- Date : 2026-03-10
- Statut : Proposed
- Périmètre : global
### Contexte
TypeScript ne valide pas les payloads HTTP au runtime (les types disparaissent à lexécution).
Quand on maintient à la fois des DTO/back (ex. Nest + decorators) et des types/contracts côté clients, on obtient une double source de vérité → drift, régressions, temps de debug.
### Options envisagées
- DTO NestJS + `class-validator` (validation runtime OK côté API, mais non partageable tel quel côté clients → duplication ou génération)
- Schémas runtime partagés dans un package `contracts` + types dérivés (validation + types alignés)
- OpenAPI/JSON Schema “first” + codegen (artefact contractuel unique, mais pipeline/outillage à maintenir)
### Décision
La source de vérité des contrats API↔clients est un package `contracts` contenant des **schémas runtime** (Zod), et les types TypeScript sont **dérivés** via `z.infer` (No-DTO / pas de redéfinition locale).
Principe opérationnel :
- validation concentrée aux frontières (ex. pipe/guard de validation côté API),
- le reste du code consomme des **types** (et non des classes DTO redondantes),
- les contrats restent “wire-level” (pas de métier, pas de stores, pas de classes Nest).
### Justification
- Un seul artefact sert à la fois de validation runtime et de typage compile-time
- Propagation des changements plus fiable (compile + tests) → réduction du temps de debug
### Conséquences
- Dépendance assumée à Zod dans `contracts`
- Les clients consomment les types; la validation côté client reste optionnelle (utile surtout sur entrées non-API : storage, deeplinks, etc.)
- En contexte non-mobile (Nest/React), OpenAPI-first + codegen reste une alternative valide si multi-clients/externe ou besoin fort de contrat public/documenté
---
<a id="decision-user-views"></a>
## User views — User public par défaut + MeUser explicite
- Date : 2026-03-10
- Statut : Proposed
- Périmètre : global
### Contexte
Un “User” complet est rarement un bon contrat universel : il peut contenir des champs sensibles (email, adresse, téléphone, flags internes…).
Les dérivations TypeScript seules (`Omit<User, "password">`) ne protègent pas au runtime et favorisent les fuites accidentelles.
### Options envisagées
- Un type `User` “god object” + dérivations TS (Omit/Pick) au cas par cas
- Des “views” explicites par contexte (public, self, admin…), dérivées depuis les schémas runtime
- Un modèle unique côté DB + sérialisation implicite (risque élevé de fuite)
### Décision
`User` (dans les contracts) est **public par défaut** et minimal (safe-by-default).
La vue self/compte est un contrat séparé `MeUser`.
Toute vue plus riche est créée explicitement et nommée (ex. `AdminUser`, `UserDirectoryEntry`).
Règles associées :
- les vues sont dérivées depuis les schémas runtime (extend/pick/omit) + `z.infer`
- `password` (et assimilés) nexiste que dans des requêtes dauth (login/register/reset/change), jamais dans un `User*`
### Justification
- Réduit le risque de fuite de données et clarifie les permissions/UX
- Évite les champs optionnels ambigus et les contrats “implicites”
### Conséquences
- Chaque endpoint choisit explicitement la vue renvoyée (et côté DB, un `select` explicite par vue)
- Les clients typent “public” vs “mon compte” distinctement
- Des tests “no secret keys” sur réponses user/auth deviennent simples et efficaces
---
## 2. Infra
<a id="decision-structure-docker"></a>
## Convention de structure pour les projets Docker et les données persistantes
- Date : 2026-03-06
- Statut : Accepted
- Périmètre : infra
### Contexte
Sur un serveur de développement (NUC / VM docker-dev), Docker mélange facilement
code applicatif, données persistantes, volumes et backups si aucune convention
nest définie.
Avec le temps cela rend :
- les sauvegardes ambiguës
- le nettoyage risqué
- la compréhension de linfrastructure difficile
- la reconstruction dun environnement compliquée
Une structure simple et explicite permet déviter ces problèmes.
### Options envisagées
- Laisser Docker gérer implicitement les volumes (`/var/lib/docker/volumes`)
- Laisser chaque projet organiser librement ses dossiers
- Définir une convention globale claire séparant code, données et sauvegardes
### Décision
La structure standard suivante est adoptée sur les machines dinfrastructure
(NUC / serveurs de développement) :
```
/srv
├ projects
├ docker-data
└ backups
```
Principes :
- `/srv/projects`
contient les projets applicatifs (code, `docker-compose.yml`, `.env`, scripts).
- `/srv/docker-data`
contient les données persistantes des conteneurs (bases de données, uploads,
état applicatif).
- `/srv/backups`
contient les dumps, archives et exports destinés à la sauvegarde.
### Justification
- séparation claire **code / données / sauvegardes**
- sauvegardes plus simples et plus fiables
- nettoyage dun projet possible sans risque pour les autres
- lisibilité immédiate de linfrastructure
- reproductibilité de lenvironnement
### Conséquences
Les conteneurs utilisent en priorité des **bind mounts explicites**, par exemple :
```txt
/srv/docker-data/monapp-postgres:/var/lib/postgresql/data
```
Les volumes Docker implicites (`/var/lib/docker/volumes`) sont évités quand la
lisibilité et la maintenabilité priment.
Les données critiques à sauvegarder se trouvent principalement dans :
```
/srv/projects
/srv/docker-data
```
Convention de nommage recommandée pour les dossiers de données :
```
ex :
rl799-postgres
monapp-redis
n8n-postgres
```
---
@@ -129,190 +482,7 @@ Cette convention est recommandée pour toutes les nouvelles VM du NUC, notamment
---
<a id="decision-story-sizing-foundations"></a>
## Story sizing — foundations bloquantes vs qualité non bloquante (CI mobile)
- Date : 2026-03-09
- Statut : Accepted
- Périmètre : global
### Contexte
Des items “infra” ont été mis dans des stories foundations depic alors quaucune story métier suivante nen dépendait (ex : CI Maestro mobile iOS/Android).
Résultat observé : story foundations “never-done”, friction et coût de contexte, sans bénéfice sur le throughput métier.
### Options envisagées
- Mettre toutes les améliorations de qualité dans foundations “par principe”
- Séparer les prérequis réellement bloquants du reste (qualité non bloquante)
### Décision
On distingue explicitement :
- **Prérequis bloquants** : à inclure dans foundations (les stories suivantes en dépendent)
- **Qualité non bloquante** : story indépendante, en parallèle ou après, sans bloquer le métier
### Justification
- Un epic doit pouvoir avancer sur le métier dès que les dépendances techniques minimales sont là
- Les chantiers “qualité” (CI mobile, perf, audits…) ont souvent une inertie qui ne doit pas geler lepic
### Conséquences
- Pour chaque AC “infra” en foundations : poser la question “la story X+1 est-elle bloquée si ce nest pas fait ?”
- Si la réponse est non : sortir lAC en story dédiée (tag qualité / infra), et la planifier à part
---
<a id="decision-code-review-adversariale"></a>
## Code review adversariale — passe dédiée en contexte frais
- Date : 2026-03-09
- Statut : Accepted
- Périmètre : global
### Contexte
Certaines issues CRITICAL sont invisibles dans le contexte dimplémentation (biais de confirmation : “je sais comment cest censé marcher”).
Elles émergent uniquement en lecture froide : fondations manquantes, usages dépréciés, invariants non respectés (ex : sessions sans TTL).
### Options envisagées
- Review “au fil de leau” dans le même contexte que limplémentation
- Review dédiée, séparée, en contexte frais (nouvelle session / nouveau modèle / reviewer différent)
### Décision
La code review doit être une passe **séparée** de limplémentation, en **contexte frais**, avec un objectif explicite : chercher des CRITICAL sans concession.
### Justification
- Les incohérences systémiques (sécurité, idempotence, TTL, drift de contracts) se détectent mieux en lecture froide
- Réduit fortement le coût de debug tardif (prod/staging) et les refactors “de fondations”
### Conséquences
- Process recommandé :
1. Dev termine limplémentation, marque la story `review`
2. Nouvelle session (contexte frais) : charger uniquement story + diff
3. Review adversariale : lister CRITICAL + mitigations
4. Corriger avant `done`
---
## Format standard dune décision
## <Titre>
- Date : YYYY-MM-DD
- Statut : Proposed | Accepted | Deprecated
- Périmètre : n8n | backend | infra | global
### Contexte
### Options envisagées
### Décision
### Justification
### Conséquences
---
<a id="decision-n8n-mini-systemes"></a>
## Workflows n8n complexes = mini-systèmes
- Date : 2025-12-19
- Statut : Accepted
- Périmètre : n8n
### Contexte
Certains workflows n8n dépassent le simple enchaînement de nodes et deviennent
de véritables systèmes applicatifs (orchestration, état, branches, retries, intégrations multiples).
### Options envisagées
- Traiter ces workflows comme de simples automatisations “low-code”
- Les considérer comme du code à part entière (discipline, patterns, doc, prudence sur upgrades)
### Décision
Les considérer comme du code.
### Justification
- Complexité réelle (logique, état, orchestration)
- Sensibilité aux versions n8n (upgrade-risk)
- Besoin de maintenabilité et de capitalisation
### Conséquences
- Prudence accrue lors des upgrades
- Documentation minimale mais ciblée
- Patterns explicitement identifiés et partagés
- On accepte dutiliser du Code (JS) quand nécessaire
---
<a id="decision-frontend-production"></a>
## Le front-end est un logiciel en production
- Date : 2026-01-25
- Statut : Accepted
- Périmètre : global
### Contexte
Les applications front-end modernes (SPA, webapps) portent une part significative
de la logique applicative : state, validations, permissions, erreurs, performance,
sécurité côté client, expérience utilisateur.
Les traiter comme une simple “couche UI” conduit régulièrement à :
- une dette technique rapide,
- des bugs difficiles à diagnostiquer,
- une expérience utilisateur incohérente,
- une maintenance coûteuse dans le temps.
### Options envisagées
- Traiter le front-end comme une couche de rendu légère, peu structurée
- Traiter le front-end comme un logiciel à part entière, avec des exigences similaires au backend
### Décision
Le front-end est traité comme un **logiciel en production**.
Il est soumis aux mêmes principes que le backend :
- architecture explicite,
- gestion des erreurs,
- conventions de code,
- tests au bon niveau,
- attention portée à la maintenabilité et à lévolution.
### Justification
- Le front concentre une logique métier et technique réelle
- Les bugs front ont un impact direct sur les utilisateurs
- La complexité augmente mécaniquement avec le produit
- Les choix initiaux conditionnent fortement la maintenabilité future
### Conséquences
- Mise en place de patterns front validés et documentés
- Gestion explicite des états UI, erreurs et formulaires
- Attention portée à la performance et à laccessibilité
- Acceptation dun léger surcoût initial pour réduire la dette long terme
---
## 3. Backend
<a id="decision-backend-production"></a>
@@ -407,91 +577,6 @@ Minimum attendu :
---
<a id="decision-contrats-sso-zod"></a>
## Single source of truth des contrats — schémas runtime partagés (Zod) + z.infer (No-DTO)
- Date : 2026-03-10
- Statut : Proposed
- Périmètre : global
### Contexte
TypeScript ne valide pas les payloads HTTP au runtime (les types disparaissent à lexécution).
Quand on maintient à la fois des DTO/back (ex. Nest + decorators) et des types/contracts côté clients, on obtient une double source de vérité → drift, régressions, temps de debug.
### Options envisagées
- DTO NestJS + `class-validator` (validation runtime OK côté API, mais non partageable tel quel côté clients → duplication ou génération)
- Schémas runtime partagés dans un package `contracts` + types dérivés (validation + types alignés)
- OpenAPI/JSON Schema “first” + codegen (artefact contractuel unique, mais pipeline/outillage à maintenir)
### Décision
La source de vérité des contrats API↔clients est un package `contracts` contenant des **schémas runtime** (Zod), et les types TypeScript sont **dérivés** via `z.infer` (No-DTO / pas de redéfinition locale).
Principe opérationnel :
- validation concentrée aux frontières (ex. pipe/guard de validation côté API),
- le reste du code consomme des **types** (et non des classes DTO redondantes),
- les contrats restent “wire-level” (pas de métier, pas de stores, pas de classes Nest).
### Justification
- Un seul artefact sert à la fois de validation runtime et de typage compile-time
- Propagation des changements plus fiable (compile + tests) → réduction du temps de debug
### Conséquences
- Dépendance assumée à Zod dans `contracts`
- Les clients consomment les types; la validation côté client reste optionnelle (utile surtout sur entrées non-API : storage, deeplinks, etc.)
- En contexte non-mobile (Nest/React), OpenAPI-first + codegen reste une alternative valide si multi-clients/externe ou besoin fort de contrat public/documenté
---
<a id="decision-user-views"></a>
## User views — User public par défaut + MeUser explicite
- Date : 2026-03-10
- Statut : Proposed
- Périmètre : global
### Contexte
Un “User” complet est rarement un bon contrat universel : il peut contenir des champs sensibles (email, adresse, téléphone, flags internes…).
Les dérivations TypeScript seules (`Omit<User, "password">`) ne protègent pas au runtime et favorisent les fuites accidentelles.
### Options envisagées
- Un type `User` “god object” + dérivations TS (Omit/Pick) au cas par cas
- Des “views” explicites par contexte (public, self, admin…), dérivées depuis les schémas runtime
- Un modèle unique côté DB + sérialisation implicite (risque élevé de fuite)
### Décision
`User` (dans les contracts) est **public par défaut** et minimal (safe-by-default).
La vue self/compte est un contrat séparé `MeUser`.
Toute vue plus riche est créée explicitement et nommée (ex. `AdminUser`, `UserDirectoryEntry`).
Règles associées :
- les vues sont dérivées depuis les schémas runtime (extend/pick/omit) + `z.infer`
- `password` (et assimilés) nexiste que dans des requêtes dauth (login/register/reset/change), jamais dans un `User*`
### Justification
- Réduit le risque de fuite de données et clarifie les permissions/UX
- Évite les champs optionnels ambigus et les contrats “implicites”
### Conséquences
- Chaque endpoint choisit explicitement la vue renvoyée (et côté DB, un `select` explicite par vue)
- Les clients typent “public” vs “mon compte” distinctement
- Des tests “no secret keys” sur réponses user/auth deviennent simples et efficaces
---
<a id="decision-erreurs-http"></a>
## Gestion standard des erreurs et des statuts HTTP
@@ -703,92 +788,41 @@ Principes :
---
<a id="decision-structure-docker"></a>
## 4. n8n
## Convention de structure pour les projets Docker et les données persistantes
<a id="decision-n8n-mini-systemes"></a>
- Date : 2026-03-06
## Workflows n8n complexes = mini-systèmes
- Date : 2025-12-19
- Statut : Accepted
- Périmètre : infra
- Périmètre : n8n
### Contexte
Sur un serveur de développement (NUC / VM docker-dev), Docker mélange facilement
code applicatif, données persistantes, volumes et backups si aucune convention
nest définie.
Avec le temps cela rend :
- les sauvegardes ambiguës
- le nettoyage risqué
- la compréhension de linfrastructure difficile
- la reconstruction dun environnement compliquée
Une structure simple et explicite permet déviter ces problèmes.
Certains workflows n8n dépassent le simple enchaînement de nodes et deviennent
de véritables systèmes applicatifs (orchestration, état, branches, retries, intégrations multiples).
### Options envisagées
- Laisser Docker gérer implicitement les volumes (`/var/lib/docker/volumes`)
- Laisser chaque projet organiser librement ses dossiers
- Définir une convention globale claire séparant code, données et sauvegardes
- Traiter ces workflows comme de simples automatisations “low-code”
- Les considérer comme du code à part entière (discipline, patterns, doc, prudence sur upgrades)
### Décision
La structure standard suivante est adoptée sur les machines dinfrastructure
(NUC / serveurs de développement) :
```
/srv
├ projects
├ docker-data
└ backups
```
Principes :
- `/srv/projects`
contient les projets applicatifs (code, `docker-compose.yml`, `.env`, scripts).
- `/srv/docker-data`
contient les données persistantes des conteneurs (bases de données, uploads,
état applicatif).
- `/srv/backups`
contient les dumps, archives et exports destinés à la sauvegarde.
Les considérer comme du code.
### Justification
- séparation claire **code / données / sauvegardes**
- sauvegardes plus simples et plus fiables
- nettoyage dun projet possible sans risque pour les autres
- lisibilité immédiate de linfrastructure
- reproductibilité de lenvironnement
- Complexité réelle (logique, état, orchestration)
- Sensibilité aux versions n8n (upgrade-risk)
- Besoin de maintenabilité et de capitalisation
### Conséquences
Les conteneurs utilisent en priorité des **bind mounts explicites**, par exemple :
```txt
/srv/docker-data/monapp-postgres:/var/lib/postgresql/data
```
Les volumes Docker implicites (`/var/lib/docker/volumes`) sont évités quand la
lisibilité et la maintenabilité priment.
Les données critiques à sauvegarder se trouvent principalement dans :
```
/srv/projects
/srv/docker-data
```
Convention de nommage recommandée pour les dossiers de données :
```
ex :
rl799-postgres
monapp-redis
n8n-postgres
```
- Prudence accrue lors des upgrades
- Documentation minimale mais ciblée
- Patterns explicitement identifiés et partagés
- On accepte dutiliser du Code (JS) quand nécessaire
---