mirror of
https://github.com/MaksTinyWorkshop/_Assistant_Lead_Tech
synced 2026-04-06 21:41:42 +02:00
14 KiB
14 KiB
Patterns back-end validés
Ce fichier contient uniquement des patterns back-end :
- testés,
- validés,
- utilisés en conditions réelles.
Objectif : éviter de réinventer la roue et réduire le temps de debug.
Dernière mise à jour : 25-01-2026
Règle d’or
Si ce n’est pas confirmé comme fonctionnel et utile, ça n’a rien à faire ici.
- Pas de “bonnes pratiques” vagues
- Pas de dépendances implicites à une stack
- Si c’est spécifique à un framework / runtime / DB : on le note
Périmètre couvert
- API (REST/GraphQL), services applicatifs
- authn/authz
- contrats (validation / schémas)
- gestion d’erreurs
- DB & migrations
- observabilité
- opérations sensibles (idempotence, retries)
- intégrations (webhooks, jobs async)
Format standard d’un pattern
Pattern :
- Objectif : …
- Contexte : …
- Quand l’utiliser : …
- Quand l’éviter : …
- Avantage : …
- Limites / vigilance : …
- Validé le : DD-MM-YYYY
- Contexte technique : (obligatoire) ex.
Node 20 / Postgres 16ouPython 3.12 / FastAPI / Redis
Implémentation (exemple minimal)
(contenu)
Checklist (si pertinente)
- Erreurs standardisées
- Validation d’entrée (schéma)
- Observabilité minimale (requestId/traceId + logs)
- Sécurité (authn/authz + secrets)
- Tests au bon niveau
- Idempotence si opération sensible
Pattern : Format d’erreur API standardisé
- Objectif : fournir des erreurs prévisibles, exploitables et cohérentes pour tous les clients.
- Contexte : API consommée par front-end, automatisations ou intégrations externes.
- Quand l’utiliser : dès qu’une API est exposée à autre chose qu’un usage interne trivial.
- Quand l’éviter : jamais.
- Avantage :
- Debug plus rapide
- UX maîtrisée côté client
- Observabilité améliorée
- Limites / vigilance :
- Discipline requise pour éviter les formats ad hoc
- Validé le : 25-01-2026
- Contexte technique : API HTTP agnostique
Implémentation (exemple minimal)
{
"error": {
"code": "USER_NOT_FOUND",
"message": "Utilisateur introuvable",
"requestId": "abc-123"
}
}
Checklist
- Codes HTTP cohérents (4xx / 5xx)
- Codes d’erreur applicatifs stables
- Message utilisateur non technique
- requestId présent
Pattern : Middleware de corrélation (requestId / traceId)
- Objectif : relier chaque requête aux logs et erreurs associées.
- Contexte : toute API ou service exposé.
- Quand l’utiliser : systématiquement en production.
- Quand l’éviter : jamais.
- Avantage :
- MTTR réduit drastiquement
- Debug cross-services possible
- Limites / vigilance :
- Doit être propagé partout (logs, erreurs, appels sortants)
- Validé le : 25-01-2026
- Contexte technique : Backend agnostique (HTTP)
Implémentation (exemple minimal)
- Générer un requestId à l’entrée si absent
- Le propager dans le contexte de requête
- L’inclure dans chaque log et réponse d’erreur
Checklist
- requestId généré ou repris d’un header existant
- Présent dans tous les logs
- Présent dans les erreurs retournées
Pattern : Idempotency key pour opérations sensibles
- Objectif : empêcher les doublons lors de retries ou timeouts.
- Contexte : création de ressources, paiements, webhooks.
- Quand l’utiliser : toute opération non strictement en lecture.
- Quand l’éviter : endpoints purement GET.
- Avantage :
- Protection contre doublons
- Robustesse face aux retries
- Limites / vigilance :
- Stockage et expiration des clés à gérer
- Validé le : 25-01-2026
- Contexte technique : API HTTP + DB transactionnelle
Implémentation (exemple minimal)
- Client fournit Idempotency-Key
- Backend stocke la clé + résultat
- Retry retourne le résultat initial
Checklist
- Clé obligatoire sur endpoints sensibles
- Contrainte d’unicité côté DB
- Comportement documenté
Pattern : Pagination robuste (cursor-based) pour les listings
- Objectif : fournir des listings stables et performants sans incohérences entre pages.
- Contexte : endpoints de liste (ex. /users, /orders) avec volume potentiellement important.
- Quand l’utiliser : dès qu’un listing peut dépasser quelques dizaines/centaines d’items ou subir des écritures concurrentes.
- Quand l’éviter : listes strictement petites et statiques.
- Avantage :
- Résultats stables malgré insertions/suppressions
- Meilleure performance que l’offset sur gros volumes
- Expérience client plus fiable
- Limites / vigilance :
- Nécessite un tri déterministe (champ + tie-breaker)
- Complexité légèrement supérieure à offset/limit
- Validé le : 25-01-2026
- Contexte technique : API HTTP + DB (Postgres/MySQL), agnostique framework
Implémentation (exemple minimal)
- Trier par (createdAt DESC, id DESC) (exemple)
- Le client envoie cursor = dernier (createdAt,id) reçu
- Le backend renvoie nextCursor si plus de résultats
- Ne jamais exposer de cursor implicite ou non documenté
Checklist
- Tri déterministe (avec tie-breaker)
- nextCursor renvoyé et documenté
- Limite max de page (protection)
- Index DB aligné avec le tri
Pattern : Exécution asynchrone des tâches longues (queue + outbox light)
- Objectif : sortir les opérations longues ou fragiles du chemin request/response.
- Contexte : envoi d’emails, appels SaaS, génération de PDF, traitements batch, webhooks sortants.
- Quand l’utiliser : dès qu’une opération peut dépasser la latence acceptable ou dépendre d’un service externe.
- Quand l’éviter : opérations réellement instantanées et sans dépendances externes.
- Avantage :
- API plus rapide et plus fiable
- Retries maîtrisés
- Meilleure résilience aux pannes externes
- Limites / vigilance :
- Demande une discipline stricte sur l’idempotence
- Nécessite une stratégie minimale de dead-letter ou d’alerting
- Validé le : 25-01-2026
- Contexte technique : Backend agnostique + DB transactionnelle + worker
Implémentation (exemple minimal)
- API écrit un job ou event en DB dans la transaction métier
- Worker lit les jobs en attente et exécute
- Retries avec backoff + compteur
- Statut FAILED ou dead-letter + alerte
- Idempotence par clé métier ou idempotency key
Checklist
- Job créé dans une transaction (évite les pertes)
- Retries et backoff définis
- Dead-letter ou statut FAILED visible
- Idempotence garantie
- Logs corrélés (requestId/traceId)
Pattern : Soft delete et archivage explicite
- Objectif : permettre la suppression logique sans perte immédiate de données.
- Contexte : données métier critiques, besoins d’audit, restauration ou conformité.
- Quand l’utiliser : dès qu’une suppression peut avoir des impacts métier ou légaux.
- Quand l’éviter : données purement techniques ou réellement éphémères.
- Avantage :
- Restauration possible
- Audit et traçabilité
- Réduction des suppressions irréversibles
- Limites / vigilance :
- Complexité accrue sur les requêtes
- Nécessite une discipline stricte (filtres par défaut)
- Validé le : 25-01-2026
- Contexte technique : API + DB relationnelle
Implémentation (exemple minimal)
- Champ deletedAt (nullable) ou status
- Les requêtes standards filtrent deletedAt IS NULL
- Endpoints dédiés pour restauration / purge
- Index DB tenant compte du soft delete
Checklist
- Filtrage soft delete par défaut
- Restauration explicite possible
- Purge maîtrisée (cron / job)
- Index DB adaptés
- Tests sur cas supprimé / restauré
Pattern : Webhooks sortants robustes et idempotents
- Objectif : garantir des intégrations fiables avec des systèmes externes.
- Contexte : notifications, synchronisations, événements métier sortants.
- Quand l’utiliser : dès qu’un événement doit être transmis à un tiers.
- Quand l’éviter : intégrations strictement synchrones et internes.
- Avantage :
- Tolérance aux pannes réseau
- Retries maîtrisés
- Observabilité des échecs
- Limites / vigilance :
- Gestion des retries et du volume
- Nécessite une idempotence côté consommateur
- Validé le : 25-01-2026
- Contexte technique : Backend + HTTP + worker/queue
Implémentation (exemple minimal)
- Événement persisté (outbox) en DB
- Envoi asynchrone via worker
- Retries avec backoff
- Signature du payload (HMAC)
- Idempotency key dans le header
Checklist
- Payload signé et vérifiable
- Retries + backoff définis
- Dead-letter ou statut FAILED visible
- 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/contractsou é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
- Ne pas mettre de logique métier dans
- Validé le : 07-03-2026
- Contexte technique : TypeScript / Zod / NestJS + Expo (React Native) — pattern agnostique framework
Implémentation (exemple minimal)
// 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<typeof RegisterRequestSchema>; // 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<typeof Schema> ZodValidationPipeglobal 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_GUARDdansproviders[]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
- L’ordre des
- Validé le : 07-03-2026
- Contexte technique : NestJS v10+
Implémentation (exemple minimal)
// 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<boolean>(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é
- Middleware de corrélation (requestId / traceId)
- Idempotency key pour opérations sensibles
- Pagination robuste (cursor-based) pour les listings
- 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
⸻
Notes importantes
- On préfère 5 patterns solides à 50 “bons conseils”.
- Un pattern = une idée actionnable + son cadre d’utilisation.