Leçons — câblage de @kanmaber/auth-backend dans jobPipeline¶
Contexte¶
Câblage réel du package partagé @kanmaber/auth-backend
(Shared/packages/auth-backend, distribué en .tgz local, pas un
registre npm) dans jobPipeline — container DI dédié, routeur Express
dédié, seed ProfilUtilisateur. Deux bugs réels ont été trouvés et
corrigés uniquement grâce à un test end-to-end réel (curl sur les
endpoints /api/auth/register et /api/auth/connexion), après que le
typecheck et le smoke-test d'assemblage DI soient passés sans rien
détecter. Suit le pattern DDD/Clean Architecture documenté dans
shared/architecture/guide-architecture-ddd (use case en 5 étapes,
repository dédié par agrégat).
Contenu¶
#cle-etrangere Leçon 1 — une valeur hardcodée utilisée comme clé étrangère est une bombe à retardement silencieuse¶
InscrireUtilisateurUseCase (dans le package) hardcodait
idRole: 'role_observateur', valeur utilisée directement comme
profilId (FK) par le repository Prisma — sans lookup, sans
validation, sans documentation nulle part (ni README, ni script de seed
livré, ni commentaire). Résultat : la première inscription réelle a
crashé avec une violation de contrainte FK Prisma brute
(utilisateurs_profilId_fkey), un message technique opaque sans rapport
avec la cause réelle.
Ça a passé le typecheck (string valide), ça a passé le smoke test (qui
n'assemble que des objets DI, jamais une vraie requête DB) — détecté
seulement au premier curl réel.
Correction : résolution dynamique via une clé métier stable et
unique (ProfilUtilisateur.code, déjà @unique dans le schéma), avec
une nouvelle méthode obtenirProfilParCode(code) ajoutée à
IUtilisateurRepository (pas de nouveau repository dédié — empreinte
minimale, cohérent avec la règle "pas d'abstraction prématurée"), et une
erreur métier explicite (PROFIL_PAR_DEFAUT_INTROUVABLE) si le seed est
absent, au lieu de laisser remonter l'erreur Prisma brute. Le code par
défaut est configurable via AuthConfig.DEFAULT_ROLE_CODE
(AUTH_DEFAULT_ROLE_CODE en env, défaut 'UTILISATEUR').
Règle générale : dans un package partagé, toute valeur qui référence une donnée censée exister côté consommateur (seed, config par défaut, rôle par défaut...) doit être résolue par une clé métier lisible, jamais un id technique arbitraire hardcodé ; et accompagnée d'une erreur claire si absente, jamais d'un crash technique opaque.
#npm Leçon 2 — npm install sur une dépendance file:...tgz peut silencieusement garder l'ancien code¶
Après correction du package, rebuild (npm run build) et regénération du
tarball (npm pack), un rm -rf node_modules/@kanmaber/auth-backend &&
npm install dans jobPipeline a bien réécrit le fichier (nouveau
timestamp) mais avec l'ancien contenu. Aucune erreur, aucun
avertissement.
Cause : package-lock.json retient un hash d'intégrité calculé à la
première installation. Tant que le chemin et le numéro de
version de la dépendance file: restent identiques, npm fait confiance
à son cache local (~/.npm/_cacache) au lieu de relire réellement les
octets du .tgz — même si ce fichier a physiquement changé entre-temps.
Option rejetée : pointer la dépendance vers le dossier source plutôt
que le .tgz (file:../../Shared/packages/auth-backend, sans nom de
fichier). npm crée alors un symlink, et Node résout les imports internes
du package (ex. @prisma/client) via le node_modules du dossier du
package plutôt que celui du consommateur. Vérifié concrètement : ce
node_modules contenait un client Prisma généré depuis le schéma
fragment du package (communeCode, updatedAt sans @updatedAt),
différent du schéma fusionné réel de jobPipeline (telephone,
titre, @updatedAt correct) — un bug de désynchronisation silencieux
bien pire que le problème de cache initial.
Solution retenue : bumper la version du package à chaque
modification (1.0.0 → 1.0.1) avant npm pack, mettre à jour la
référence file:...tgz côté consommateur, supprimer l'ancien .tgz.
Un nouveau numéro de version force npm à traiter le contenu comme neuf,
sans passer par le cache. Vérifié : après bump, npm install seul (sans
extraction manuelle du tarball) a correctement repris le nouveau code.
Règle générale pour tout package interne file: + tarball local :
- toujours bumper la version avant npm pack, même en dev interne ;
supprimer l'ancien .tgz pour éviter toute ambiguïté ;
- ne pas pointer vers le dossier source tant que le package a son propre
node_modules avec des dépendances qui pourraient diverger de celles
du consommateur (notamment @prisma/client, généré depuis un schéma) ;
- après tout rebuild, vérifier explicitement (grep sur une chaîne
caractéristique du changement) que le contenu installé correspond bien
à la nouvelle version — ne jamais supposer que npm install a marché.
Leçon 3 (rappel) — le typecheck et les smoke tests ne prouvent pas qu'une fonctionnalité marche¶
Les deux bugs ci-dessus, plus des champs Zod requis non documentés
(confirmationMotDePasse, accepteCGU) dans le payload d'inscription,
sont passés inaperçus jusqu'au premier vrai test curl de bout en bout.
Confirme la pratique déjà en usage : tester le comportement réel avant de
considérer un câblage terminé, pas seulement la compilation.
Décision / Conclusion¶
- Option C retenue pour la résolution du rôle par défaut : lookup par
codevia une méthode ajoutée àIUtilisateurRepositoryexistant (pas de nouveau repository dédié). - Distribution du package
file:...tgzconservée telle quelle (pas de passage au dossier symlinké) — le risque de désynchronisation Prisma l'emporte sur le confort de développement. - Discipline adoptée : bump de version systématique à chaque modification du package avant repack.
À faire¶
- Checklist réutilisable pour tout futur package
Shared/packages/*: - valeur de référence hardcodée → résolue par clé métier, jamais un id fixe ;
- le package documente (README/seed) toute donnée supposée côté consommateur ;
- version bumpée à chaque modif avant
npm pack, ancien.tgzsupprimé ; - après
npm install, vérifier par grep que le contenu correspond à la nouvelle version ; - test end-to-end réel (curl/navigateur) avant de considérer le câblage terminé.
- Problème SMTP restant à traiter (rejet
550 SPAMlors de l'envoi de l'email de vérification) — voir veille-financement/configuration-email-deliverabilite pour un cas très similaire déjà rencontré (rejet 550, migration vers Cloudflare Email Routing / Brevo).
Liens connexes¶
- shared/architecture/guide-architecture-ddd — référence DDD (use case 5 étapes, repository par agrégat) suivie par le package
- DDD_Module/debug/erreurs-deploiement-resolu — autre liste de pièges de déploiement résolus (npm, CJS/ESM), même esprit de capitalisation
- veille-financement/configuration-email-deliverabilite — cas similaire de rejet SMTP 550, pertinent pour le prochain problème à traiter (SMTP jobPipeline)
- shared/flow-connexion-auth — flow auth transversal (login, 2FA, refresh JWT) auquel ce package correspond
- jobPipeline/index — index du projet parent (à créer)