diff --git a/10_backend_patterns_valides.md b/10_backend_patterns_valides.md index dfef052..1eba97c 100644 --- a/10_backend_patterns_valides.md +++ b/10_backend_patterns_valides.md @@ -300,6 +300,129 @@ Si ce n’est pas confirmé comme fonctionnel et utile, **ça n’a rien à fair - Idempotence documentée - Logs corrélés (requestId/traceId) +--- + +## Pattern : Contracts-First / Zod-Infer / No-DTO (monorepo TypeScript fullstack) + +- Objectif : avoir une seule source de vérité pour les contrats d’interface entre API et client, sans redéfinition manuelle de types. +- Contexte : monorepo TypeScript avec un package partagé (`packages/contracts` ou équivalent), consommé par le backend et le front/mobile. +- Quand l’utiliser : dès qu’une API est consommée par un client TypeScript dans le même repo. +- Quand l’éviter : si le client est externe (autre organisation, autre langage) — dans ce cas, OpenAPI reste la référence. +- Avantage : + - Zéro drift entre contrat et implémentation + - Types TypeScript gratuits via `z.infer<>` — aucune réécriture + - Changement de contrat = erreur de compilation immédiate côté client + - Mocks de tests alignés automatiquement +- Limites / vigilance : + - Ne pas mettre de logique métier dans `packages/contracts` (IO only) + - Attention aux dépendances circulaires si le package grossit +- Validé le : 07-03-2026 +- Contexte technique : TypeScript / Zod / NestJS + Expo (React Native) — pattern agnostique framework + +### Implémentation (exemple minimal) + +```typescript +// packages/contracts/src/auth/auth.schemas.ts +export const RegisterRequestSchema = z.object({ + email: z.string().email(), + password: z.string().min(8), +}); +export type RegisterRequest = z.infer; // type GRATUIT + +// packages/contracts/src/index.ts +export * from ‘./auth/auth.schemas’; +export * from ‘./errors/error-code’; + +// apps/api/src/modules/auth/auth.controller.ts +import type { RegisterRequest } from ‘@monrepo/contracts’; +// + ZodValidationPipe → validation automatique, zéro DTO manuel + +// apps/mobile/src/domains/auth/auth.store.ts +import type { RegisterRequest } from ‘@monrepo/contracts’; +// même type, même schéma, zéro duplication +``` + +### Structure cible du package contracts + +``` +packages/contracts/src/ + auth/auth.schemas.ts ← request/response auth + users/users.schemas.ts ← request/response users + billing/billing.schemas.ts ← request/response billing (Epic suivant) + errors/error-code.ts ← enum codes d’erreur stables + http/envelopes.ts ← { data, meta } / { error, meta } + index.ts ← re-export tout +``` + +### Ce qui appartient à contracts + +- Schémas Zod request/response +- Types inférés (`z.infer<>`) +- Codes d’erreur applicatifs stables +- Enums et constantes partagées (ex : liste officielle de sujets/topics) + +### Ce qui n’appartient PAS à contracts + +- Logique métier +- Modules/services/guards framework (NestJS, etc.) +- State management client (Zustand, Redux, etc.) + +### Checklist + +- [ ] Zéro DTO manuel dans l’API — uniquement `z.infer` +- [ ] `ZodValidationPipe` global ou par endpoint pour la validation d’entrée +- [ ] Constantes partagées (enums, listes) dans contracts, jamais dupliquées +- [ ] Mocks de tests importent les types depuis contracts + +--- + +## Pattern : Guard global NestJS — ordre d’enregistrement et décorateurs de bypass + +- Objectif : protéger tous les endpoints par défaut, avec un mécanisme explicite pour les exceptions. +- Contexte : API NestJS avec plusieurs guards globaux (authn, authz, feature flags...). +- Quand l’utiliser : dès qu’on a 2+ guards globaux dont l’un dépend du résultat de l’autre. +- Quand l’éviter : si un seul guard suffit. +- Avantage : + - Sécurité par défaut (opt-out, pas opt-in) + - Ordre d’exécution garanti et explicite + - Bypass documenté et traçable via décorateurs +- Limites / vigilance : + - L’ordre des `APP_GUARD` dans `providers[]` est l’ordre d’exécution — ne pas inverser + - Exporter le service depuis son module si injecté dans un guard global d’un autre module +- Validé le : 07-03-2026 +- Contexte technique : NestJS v10+ + +### Implémentation (exemple minimal) + +```typescript +// app.module.ts +providers: [ + { provide: APP_GUARD, useClass: AuthGuard }, // 1er : peuple request.user + { provide: APP_GUARD, useClass: EmailVerifiedGuard }, // 2ème : lit request.user + { provide: APP_GUARD, useClass: EntitlementsGuard }, // 3ème : lit request.user + entitlements +] + +// skip-auth.decorator.ts +export const SKIP_AUTH = ‘skipAuth’; +export const SkipAuth = () => SetMetadata(SKIP_AUTH, true); + +// auth.guard.ts +const skip = this.reflector.getAllAndOverride(SKIP_AUTH, [ + context.getHandler(), + context.getClass(), // permet @SkipAuth() au niveau classe +]); +if (skip) return true; +``` + +### Checklist + +- [ ] AuthGuard enregistré en premier dans `providers[]` +- [ ] AuthModule exporte AuthService si AuthGuard est dans AppModule +- [ ] Décorateur `@SkipAuth()` sur tous les endpoints publics (auth, health, docs) +- [ ] Tests unitaires sur le guard avec reflector mocké + +--- + ### Index (à remplir au fil des validations) - Format d’erreur API standardisé @@ -309,6 +432,8 @@ Si ce n’est pas confirmé comme fonctionnel et utile, **ça n’a rien à fair - Exécution asynchrone des tâches longues (queue + outbox light) - Soft delete et archivage explicite - Webhooks sortants robustes et idempotents +- Contracts-First / Zod-Infer / No-DTO (monorepo TypeScript fullstack) +- Guard global NestJS — ordre d’enregistrement et décorateurs de bypass ⸻ diff --git a/10_frontend_patterns_valides.md b/10_frontend_patterns_valides.md index 34e54b7..325f086 100644 --- a/10_frontend_patterns_valides.md +++ b/10_frontend_patterns_valides.md @@ -197,11 +197,91 @@ Ne jamais : - [ ] États loading / disabled gérés - [ ] Tests sur cas valides et invalides +--- + +## Pattern : Navigation réactive post-action async (React / Expo Router) + +### Synthèse + +- **Objectif** : déclencher la navigation après une action asynchrone (login, register, submit) de façon idiomatique et sans bypasser la réactivité React. +- **Contexte** : SPA ou app mobile React avec state management (Zustand, Redux, Context) et router déclaratif (React Router, Expo Router, Next.js App Router). +- **Quand l'utiliser** : dès qu'une navigation dépend du résultat d'une action async. +- **Quand l'éviter** : navigations synchrones sans état async impliqué. + +### Analyse + +- **Avantages** : + - Respecte le cycle de vie React (pas de lecture de state hors cycle) + - Re-render automatique si l'état change entre-temps + - Testable : on peut assert sur l'état, pas sur des effets de bord +- **Limites / vigilance** : + - Ne pas oublier les dépendances du `useEffect` (ESLint react-hooks/exhaustive-deps) + - Gérer le cas "composant démonté" si la navigation peut être annulée + +### Validation + +- Validé le : 07-03-2026 +- Contexte technique : React 18+ / Zustand / Expo Router — pattern applicable sur React Router, Next.js App Router + +### Implémentation (exemple minimal) + +```typescript +// ❌ Anti-pattern : lecture de state hors cycle React +const handleSubmit = async () => { + await login(email, password); + const { accessToken } = useAuthStore.getState(); // bypasse la réactivité + if (accessToken) router.replace('/(tabs)'); +}; + +// ✅ Pattern correct : useEffect réactif sur le state +const { accessToken, isLoading, error } = useAuthStore(); + +useEffect(() => { + if (accessToken && !isLoading && !error) { + router.replace('/(tabs)'); + } +}, [accessToken, isLoading, error]); + +const handleSubmit = async () => { + await login(email, password); + // la navigation se déclenche via useEffect quand le store se met à jour +}; +``` + +### Pour les callbacks OAuth (ref nécessaire) + +```typescript +// Quand un callback externe déclenche la navigation +const pendingOAuth = useRef(false); + +useEffect(() => { + if (pendingOAuth.current && accessToken) { + pendingOAuth.current = false; + router.replace('/(tabs)'); + } +}, [accessToken]); + +const handleOAuth = async () => { + pendingOAuth.current = true; + await exchangeWithIdp(token); +}; +``` + +### Checklist + +- [ ] Aucun `store.getState()` utilisé pour lire l'état post-action dans un handler +- [ ] `useEffect` avec dépendances explicites (state pertinent + isLoading + error) +- [ ] Cas d'erreur géré (ne pas naviguer si error est défini) +- [ ] `useRef` si le trigger vient d'un callback externe (OAuth, deep link) + +--- + ### Index des patterns - Gestion explicite des états UI (loading / empty / error) - Séparation claire server state / client state - Formulaire robuste avec validation et erreurs explicites +- Navigation réactive post-action async (React / Expo Router) ⸻ diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..0713c02 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,59 @@ +# Instructions globales — Lead Tech Copilote +# Chargé automatiquement par OpenAI Codex CLI + +## Rôle et posture + +Tu es mon copilote principal : technicien, lead tech, coach et challenger. +Priorité absolue : justesse, robustesse, réduction du temps de debug. +Jamais de sur-ingénierie. Jamais d'invention de comportements incertains. + +Langue de travail : **français**. + +## Base de connaissance à consulter en priorité + +Ces fichiers sont la mémoire durable inter-projets. Consulte-les avant de proposer +une solution dans leur domaine respectif. + +| Fichier | Contenu | +|---|---| +| `10_backend_patterns_valides.md` | Patterns backend validés en conditions réelles | +| `10_frontend_patterns_valides.md` | Patterns frontend/mobile validés | +| `10_backend_risques_et_vigilance.md` | Risques et anti-patterns backend | +| `10_frontend_risques_et_vigilance.md` | Risques et anti-patterns frontend | +| `40_decisions_et_archi.md` | Décisions techniques (mini-ADR) | +| `90_debug_et_postmortem.md` | Post-mortems et bugs capitalisés | + +## Règles de mise à jour + +Quand tu repères qu'un pattern mérite d'être capitalisé : + +``` +FILE_UPDATE_PROPOSAL +Fichier : `` +Pourquoi : <1-2 phrases> +``` + +Puis propose le contenu à ajouter dans le format du fichier cible. + +## Projets actifs + +| Projet | Stack | Localisation | État | +|---|---|---|---| +| app-alexandrie | NestJS + Expo (React Native) + Prisma + pnpm monorepo | `/srv/projects/app-alexandrie` (NUC) | Epic 2 en préparation | + +## Patterns clés à appliquer systématiquement + +- **Contracts-First / Zod-Infer / No-DTO** : voir `10_backend_patterns_valides.md` +- **Navigation réactive useEffect** : voir `10_frontend_patterns_valides.md` +- **Guard NestJS — ordre d'enregistrement** : voir `10_backend_patterns_valides.md` +- **Format d'erreur API standardisé** : `{ error: { code, message, requestId } }` +- **Sessions avec TTL** : toujours un champ `expiresAt`, filtrer dans les queries + +## Infrastructure NUC + +Convention de structure Docker sur le NUC (Proxmox) : +- `/srv/projects` — code applicatif +- `/srv/docker-data` — données persistantes (bind mounts explicites) +- `/srv/backups` — dumps et archives + +Éviter SQL Server en LXC Proxmox → préférer PostgreSQL/MariaDB (voir `90_debug_et_postmortem.md`). diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..bb00ce5 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,61 @@ +# Instructions globales — Lead Tech Copilote + +Ce fichier est chargé automatiquement par Claude Code à chaque session. +Il pointe vers la base de connaissance commune à tous les projets. + +## Rôle et posture + +Tu es mon copilote principal : technicien, lead tech, coach et challenger. +Priorité absolue : justesse, robustesse, réduction du temps de debug. +Jamais de sur-ingénierie. Jamais d'invention de comportements incertains. + +Langue de travail : **français**. + +## Base de connaissance à consulter en priorité + +Ces fichiers sont la mémoire durable inter-projets. Consulte-les avant de proposer +une solution dans leur domaine respectif. + +| Fichier | Contenu | +|---|---| +| `10_backend_patterns_valides.md` | Patterns backend validés en conditions réelles | +| `10_frontend_patterns_valides.md` | Patterns frontend/mobile validés | +| `10_backend_risques_et_vigilance.md` | Risques et anti-patterns backend | +| `10_frontend_risques_et_vigilance.md` | Risques et anti-patterns frontend | +| `40_decisions_et_archi.md` | Décisions techniques (mini-ADR) | +| `90_debug_et_postmortem.md` | Post-mortems et bugs capitalisés | + +## Règles de mise à jour + +Quand tu repères qu'un pattern mérite d'être capitalisé : + +``` +FILE_UPDATE_PROPOSAL +Fichier : `` +Pourquoi : <1-2 phrases> +``` + +Puis propose le contenu à ajouter dans le format du fichier cible. + +## Projets actifs + +| Projet | Stack | Localisation | État | +|---|---|---|---| +| app-alexandrie | NestJS + Expo (React Native) + Prisma + pnpm monorepo | `/Volumes/TeraSSD/Projets_Dev/__Mindleaf/app-alexandrie` | Epic 2 en préparation | + +## Patterns clés à appliquer systématiquement + +- **Contracts-First / Zod-Infer / No-DTO** : voir `10_backend_patterns_valides.md` +- **Navigation réactive useEffect** : voir `10_frontend_patterns_valides.md` +- **Guard NestJS — ordre d'enregistrement** : voir `10_backend_patterns_valides.md` +- **Format d'erreur API standardisé** : `{ error: { code, message, requestId } }` +- **Sessions avec TTL** : toujours un champ `expiresAt`, filtrer dans les queries + +## Infrastructure NUC + +Convention de structure Docker sur le NUC (Proxmox) : +- `/srv/projects` — code applicatif +- `/srv/docker-data` — données persistantes (bind mounts explicites) +- `/srv/backups` — dumps et archives + +Éviter SQL Server en LXC Proxmox → préférer PostgreSQL/MariaDB (voir `90_debug_et_postmortem.md`).