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,7 +8,7 @@ Ce fichier contient **uniquement** des patterns back-end :
Objectif : éviter de réinventer la roue et réduire le temps de debug.
Dernière mise à jour : 10-03-2026
Dernière mise à jour : 12-03-2026
---
@@ -31,6 +31,7 @@ Dernière mise à jour : 10-03-2026
- [Sémantique explicite `Trial` vs `Paid` dans Subscription](#pattern-subscription-trial-vs-paid)
- [Restauration dachats Stripe en 3 étapes](#pattern-restauration-achats-stripe)
- [Mapping explicite de `P2002` Prisma sur update de champ unique](#pattern-prisma-p2002-update-unique)
- [Autorisation interne minimale sans RBAC complet](#pattern-autorisation-interne-minimale)
---
@@ -88,6 +89,7 @@ Si ce nest pas confirmé comme fonctionnel et utile, **ça na rien à fair
---
<a id="pattern-format-derreur-api-standardise"></a>
## Pattern : Format derreur API standardisé
- Objectif : fournir des erreurs prévisibles, exploitables et cohérentes pour tous les clients.
@@ -125,6 +127,7 @@ Si ce nest pas confirmé comme fonctionnel et utile, **ça na rien à fair
---
<a id="pattern-middleware-correlation-requestid-traceid"></a>
## Pattern : Middleware de corrélation (requestId / traceId)
- Objectif : relier chaque requête aux logs et erreurs associées.
@@ -156,6 +159,7 @@ Si ce nest pas confirmé comme fonctionnel et utile, **ça na rien à fair
---
<a id="pattern-idempotency-key-operations-sensibles"></a>
## Pattern : Idempotency key pour opérations sensibles
- Objectif : empêcher les doublons lors de retries ou timeouts.
@@ -187,6 +191,7 @@ Si ce nest pas confirmé comme fonctionnel et utile, **ça na rien à fair
---
<a id="pattern-pagination-robuste-cursor-based"></a>
## Pattern : Pagination robuste (cursor-based) pour les listings
- Objectif : fournir des listings stables et performants sans incohérences entre pages.
@@ -222,6 +227,7 @@ Si ce nest pas confirmé comme fonctionnel et utile, **ça na rien à fair
---
<a id="pattern-execution-asynchrone-taches-longues"></a>
## Pattern : Exécution asynchrone des tâches longues (queue + outbox light)
- Objectif : sortir les opérations longues ou fragiles du chemin request/response.
@@ -259,6 +265,7 @@ Si ce nest pas confirmé comme fonctionnel et utile, **ça na rien à fair
---
<a id="pattern-soft-delete-archivage-explicite"></a>
## Pattern : Soft delete et archivage explicite
- Objectif : permettre la suppression logique sans perte immédiate de données.
@@ -295,6 +302,7 @@ Si ce nest pas confirmé comme fonctionnel et utile, **ça na rien à fair
---
<a id="pattern-webhooks-sortants-robustes-idempotents"></a>
## Pattern : Webhooks sortants robustes et idempotents
- Objectif : garantir des intégrations fiables avec des systèmes externes.
@@ -332,6 +340,7 @@ Si ce nest pas confirmé comme fonctionnel et utile, **ça na rien à fair
---
<a id="pattern-contracts-first-zod-infer-no-dto"></a>
## Pattern : Contracts-First / Zod-Infer / No-DTO (monorepo TypeScript fullstack)
- Objectif : avoir une seule source de vérité pour les contrats dinterface entre API et client, sans redéfinition manuelle de types.
@@ -407,6 +416,7 @@ packages/contracts/src/
---
<a id="pattern-guard-global-nestjs"></a>
## Pattern : Guard global NestJS — ordre denregistrement et décorateurs de bypass
- Objectif : protéger tous les endpoints par défaut, avec un mécanisme explicite pour les exceptions.
@@ -455,6 +465,7 @@ if (skip) return true;
---
<a id="pattern-provider-strategy-integrations-tierces"></a>
## Pattern : Provider-Strategy pour intégrations tierces — périmètre complet
- Objectif : isoler intégralement la logique propre à un prestataire (Stripe, Brevo, Firebase…) derrière une interface stable, pour éviter la contamination du domaine par le SDK tiers.
@@ -502,6 +513,7 @@ async handleWebhook(rawBody: Buffer, signature: string): Promise<void> {
---
<a id=”pattern-stripe-subscription-metadata”></a>
## Pattern : Stripe — metadata sur `subscription_data`, pas sur la Session
- Objectif : garantir que `userId` (ou tout identifiant métier) soit accessible dans les events `customer.subscription.*`, pas seulement dans `checkout.session.completed`.
@@ -515,14 +527,15 @@ async handleWebhook(rawBody: Buffer, signature: string): Promise<void> {
```typescript
stripe.checkout.sessions.create({
metadata: { userId }, // pour checkout.session.completed
subscription_data: { metadata: { userId } }, // pour customer.subscription.*
metadata: { userId }, // pour checkout.session.completed
subscription_data: { metadata: { userId } }, // pour customer.subscription.*
});
```
---
<a id=”pattern-webhook-parsing-unique”></a>
## Pattern : Webhooks entrants — parsing unique (single `constructWebhookEvent`)
- Objectif : appeler `constructWebhookEvent` une seule fois par requête, puis router vers des extracteurs purs.
@@ -545,6 +558,7 @@ handlePackWebhookEvent(event): PackWebhookResult | null
---
<a id=”pattern-contracts-error-codes”></a>
## Pattern : Contracts-First — error codes comme contrat obligatoire
- Objectif : maintenir les codes derreur API dans `packages/contracts` pour éviter les clients stringly-typed.
@@ -563,6 +577,7 @@ handlePackWebhookEvent(event): PackWebhookResult | null
---
<a id="pattern-redis-health-cache-court"></a>
## Pattern : RedisHealthService avec cache interne court
- Objectif : exposer un état Redis exploitable par les guards globaux sans ping Redis à chaque requête.
@@ -596,6 +611,7 @@ handlePackWebhookEvent(event): PackWebhookResult | null
---
<a id="pattern-subscription-trial-vs-paid"></a>
## Pattern : Sémantique explicite `Trial` vs `Paid` dans Subscription
- Objectif : aligner le modèle métier, les guards et les jeux de tests sur une définition unique de labonnement payant actif.
@@ -627,6 +643,7 @@ handlePackWebhookEvent(event): PackWebhookResult | null
---
<a id="pattern-restauration-achats-stripe"></a>
## Pattern : restauration dachats Stripe en 3 étapes
- Objectif : reconstruire un état local cohérent à partir de Stripe sans dépendre dune hypothèse fragile.
@@ -658,6 +675,7 @@ handlePackWebhookEvent(event): PackWebhookResult | null
---
<a id="pattern-prisma-p2002-update-unique"></a>
## Pattern : mapping explicite de `P2002` Prisma sur update de champ unique
- Objectif : transformer un conflit dunicité prévisible en erreur métier exploitable plutôt quen 500 opaque.
@@ -688,6 +706,43 @@ handlePackWebhookEvent(event): PackWebhookResult | null
---
<a id="pattern-autorisation-interne-minimale"></a>
## Pattern : Autorisation interne minimale sans RBAC complet
- Objectif : sécuriser une capacité interne sensible sans ouvrir trop tôt un chantier RBAC complet.
- Contexte : application avec peu de rôles, besoin ponctuel dune capacité admin ou opérateur clairement identifiée.
- Quand lutiliser : quand une story métier demande un pouvoir interne limité mais réel.
- Quand léviter : si les permissions deviennent nombreuses, hiérarchiques ou contextuelles.
- Avantage :
- sécurisation rapide et lisible dune capacité sensible
- source de vérité backend explicite
- chemin dévolution propre vers un RBAC plus complet
- Limites / vigilance :
- ne pas laisser proliférer des rôles ad hoc non gouvernés
- ne remplace pas un vrai modèle de permissions si le domaine grossit
- Validé le : 10-03-2026
- Contexte technique : NestJS / auth par session ou JWT / API métier interne
### Implémentation (exemple minimal)
```txt
- introduire un enum de rôle minimal côté backend (ex. USER | ADMIN)
- propager ce rôle dans la session ou le token dauth
- créer un décorateur + guard dédiés pour la capacité sensible
- interdire les booléens front, emails hardcodés ou `if` dispersés dans les contrôleurs
```
### Checklist
- Le rôle vit dans la source de vérité backend
- Le rôle est propagé dans le mécanisme dauth existant
- Les endpoints sensibles passent par un guard dédié
- Aucun contrôle daccès critique nest piloté par le front
- Le passage à RBAC reste possible sans casser le contrat existant
---
### Notes importantes
- On préfère 5 patterns solides à 50 “bons conseils”.