Aller au contenu

Conventions de nommage

Source de vérité du nommage pour projects/api. But : sémantique déterministe — pour un couple (couche + intention + type) il existe un seul nom correct, et un seul terme par concept (zéro synonyme). Vaut pour les agents comme pour l'équipe.

S'applique au nouveau code (src/__new/). Conforme à la règle absolue : ne jamais renommer l'existant sans demande explicite.

DRY — ne pas redéclarer, lier. Déjà réglé ailleurs : - Fichiers (*.repository.ts…), service objects (PascalCase sans Factory), namespace Model, IDs brandés, codes d'erreur, préfixe ky_*api-conventions.mdc - Branded types, Err('code' as const), zod (nouveau) vs idtlt (existant) → .cursor/rules/generic-monorepo-rules.mdc - Sémantique des entités métier → domain-map.mdc


Lookup rapide (verbe par intention)

Intention Couche Nom
Lire 1 entité, présence attendue repository getOne / getXById
Lire 1 entité, peut être absente repository findOneByXT \| undefined
Lire 1 entité + verrou (FOR UPDATE) repository getAndLock…
Lire N entités / collection repository getMany / getAll
Compter repository countX
Existence (booléen) repository hasX / existsX
Insérer repository insertOne / insertMany
Modifier repository updateOne / updateMany
Supprimer (soft préféré) repository deleteOne
Orchestrer un workflow async / transaction service processX
Tirer un état externe → local service syncX
Créer une entité / un graphe service createX
Calcul pur business computeX
Sélection d'une règle / config business resolveX
Application d'un jeu de règles → décision business evaluateX
Prédicat (partout) toutes isX / hasX / canX

1. Verbes par couche

Repository — lecture

  • get* par défaut : présence attendue (assert/throw si absent). getOne, getMany, getAll, getXById.
  • find* uniquement nullable-by-design : la valeur peut légitimement ne pas exister → retour T | undefined (findOneByEmail). Le verbe porte l'information « peut être absent ».
  • getAndLock* seul pour les lectures avec FOR UPDATE (pas findOneAndLock).
  • count* pour les agrégats scalaires ; has* / exists* pour les booléens.

Repository — écriture

  • insertOne / insertMany (plier insertBatch dans insertMany sauf payload réellement distinct).
  • updateOne / updateMany.
  • deleteOne : hard delete. Soft-delete préféré (statut / timestamp deletedAt) — c'est déjà le pattern de facto.
  • Pas de create / save / mark au niveau repository : ce sont des verbes service.

Service (un gagnant par intention)

get (lecture), create (nouvelle entité/graphe), update (merge de champs sur un existant), set (flag / champ unique), compute (dérivation pure — déléguée à business/), generate (artefact / fichier), save (persiste un agrégat de haut niveau), sync (PULL d'un état externe → local), process (workflow async / transaction — gagnant face à handle/execute/run/perform), transfer (mouvement de fonds), confirm (commit d'une action utilisateur), send (message sortant), schedule (job différé), trigger (fire-and-forget). Prédicats : can (éligibilité), is (état), has (existence).

Business (fonctions pures)

compute (arithmétique pure), resolve (lookup / sélection d'une config ou règle), evaluate (application d'un jeu de règles → objet décision) — distincts, ne pas fusionner. Prédicats : canX (→ Result / booléen), assert (throw sur précondition). Transition d'état du domaine = verbe métier court sur l'entité (PendingX.succeed(), .retry()).

Controller

Handler aligné sur le verbe HTTP : get() / list() pour @Get, create() pour @Post, update() pour @Patch, delete() pour @Delete. Transition d'état via @Put :id/<action> (ex. /complete). Upload en deux temps : request-uploadconfirm-upload. Éviter POST /create-* : un @Post sur la ressource implique déjà la création.


2. Suffixes de type

  • Entrée (create/update) → Payload : XCreatePayload, XUpdatePayload. Input réservé aux données de template / formulaire / PDF. Dto banni (legacy). Body autorisé uniquement en stack Zod (*BodySchema) — marqueur de stack assumé.
  • Sortie APIResponse, namespacée dans le modèle :
export namespace InvestorPortfolio {
  export type Response = { invested: Cents; brickCount: number }
}

View réservé aux fonctions de projection qui construisent une Response. Ne jamais renvoyer Model.T brut depuis un controller dès qu'on ajoute des champs calculés/agrégés. - Alias d'entitétype T = typeof validator.T dans le namespace Model (idiomatique). Pas de suffixe Entity / Row / Model sur l'alias. Alias PascalCase nommés seulement pour les variantes d'une discriminated union.


3. Booléens

Préfixe obligatoire is / has / can / should sur tout champ booléen et tout prédicat. Pas d'adjectif nu : isEnabled (pas enabled), isBlocked, isAvailable, isHighlighted, isFlagged.


4. Variables

  • Collections → nom pluriel exclusivement (documents, properties, brickIds). Bannir les suffixes -List / -Array / -Collection.
  • Map / lookup → fonction getXById / findXById ; une map pré-construite en mémoire = xById (propertyNamesById) ; suffixe *Map seulement si la clé n'est pas un id (countryCodesMap).
  • Compteurs / quantités*Count (compteur d'items : brickCount), *Total (somme agrégée : totalFundedAmount), *Amount (montant monétaire : repaymentAmount). Bannir nb* en TS (alias SQL uniquement → mapper en xCount au repository).
  • Génériques bannis → voir api-conventions.mdc (data/info/value/details) ; idem result / tmp / obj hors d'un scope local trivial.

5. Suffixes de champ

Timestamps → *At (createdAt, deletedAt, attemptedAt). Dates sans heure → *Date.

Montants : tout est en centimes en DB → s'appuyer sur le brand Cents (ex. invested: Cents, brickPrice: Cents), pas de suffixe *Cents qui ré-encode l'unité déjà portée par le type. Sur un champ de contrat JSON le brand ne survit pas à la sérialisation → ajouter .describe('… en centimes').


6. Lexique des statuts

Un seul terme canonique par concept. Champ discriminant = status (jamais state ni kind).

Concept Canonique Bannir (synonymes constatés)
Réussi / confirmé confirmed succeeded*, successful, completed, ok, validated
Payout réglé paid
En attente, pas commencé pending processing, queued
Attente bloquante (événement externe) waiting waiting_for_* dans la valeur (cf. ci-dessous)
Traitement actif in_progress processing, ongoing
Échec système failed error (réservé aux codes), ko
Rejet métier declined rejected, refused
Remboursé / contre-passation refunded reversed
Bloqué (fraude / conformité) blocked
Annulé canceled cancelled, aborted, voided
Expiré expired
Planifié scheduled notScheduled
Affecté / alloué assigned
Stocké stored
Supprimé (soft) deleted
Acheté purchased
Réservé reserved
Approuvé approved

* succeeded : exception runtime tolérée pour lemonway-p2p (enum d'un provider déjà déployé). Aligner le fichier (successful-lemonway-p2p.ts) et le type (SucceededLemonwayP2P) sur la valeur succeeded, et documenter la traduction succeeded ↔ confirmed au bord wallet/p2p. Règle : traduire au bord, pas migrer un enum de paiement live.

Interdits transverses - Négations : notScheduled / noFees / notExecuted → état positif + flag séparé (hasFees). - Surcharge : un même littéral pour succès et échec (executed) → séparer en deux valeurs. - Casing mixte dans une même entité.

Casing - Valeurs de statut → snake_case (waiting_for_assignation). - Codes d'erreur → kebab-case + dot-namespacing (déjà réglé, ne pas redéclarer). - Sous-états fins (waiting_for_card_confirmation…) : OK pour une machine à états interne, mais pour une surface API pousser la spécificité dans un champ « raison » séparé plutôt que dans la valeur de status.


7. Vocabulaire de domaine

Un seul terme par concept — tout synonyme est banni. Deux règles transverses :

  • Legacy figé : les bannis marqués (legacy : …) restent en place (tables, FK, modules — règle absolue → api-conventions.mdc) mais ne nomment jamais du code neuf.
  • Métier FR jamais traduit : un concept réglementaire/contractuel français garde son nom FR, même absent de cette table ; les valeurs de status restent EN — mensualiteStatus: 'paid', jamais 'payee'.
  • Enforcement : le sous-ensemble machine-vérifiable est linté sur les lignes ajoutées uniquement (declarations, pas les références aux noms figés) par scripts/lint-conventions/lint-domain-vocabulary.ts (pnpm lint + pre-commit). Source de vérité de ce sous-ensemble : scripts/lint-conventions/domain-vocabulary.json — nouvelle paire à enforcer = l'ajouter au JSON, pas ici.
Concept Canonique Bannir
Unité de propriété brick share, unit
Investisseur investor user ; customer (legacy : table customers — aucune table investors —, modules customer-*)
Projet immobilier (véhicule de financement / investissement) project property (legacy : tables properties / property_*, FK propertyId, modules property-*)
Porteur de projet ProjectOwner operator, manager
Espace financement porteur project-financing-request espace-fi (legacy abandonné)
Société emprunteuse (espace financement) ProjectOwnerCompany (project_owner_company) SPV / EspaceFiSpv (legacy EF — distinct du SPV investisseur special_purpose_vehicule)
Transfert investisseur ↔ investisseur p2p
Conteneur de registre transaction
Capital de développement funding collecte, collect
Entrée initiale investisseur money-in deposit (nouveau code)
Opération de retrait withdraw (forme verbale) withdrawal (legacy : table investor_withdraw_requests, WT kind withdrawal_legacy)
Versement sortant payout versement, reversement
Frais fee / fees frais (hors littéraux fiscaux/facturation)
Mandat SEPA Mandate (SddMandate) mandat
Rendement / retours investment (réservé à ce contexte)
Calendrier de remboursement echeancier (echeancierConfig, EcheancierViewService) schedule, ScheduleConfig (legacy : module property-payment-schedule)
Ligne d'échéance echeance (Echeance, EcheanceId, EcheanceStatus) installment
Mensualité mensualite (mensualiteStatus) monthlyPayment
Tirage sur budget travaux tirage (terme FR, comme echeancier) drawdown, draw
Budget travaux (enveloppe travaux) constructionBudget (totalConstructionBudget) « séquestre travaux » — jamais « séquestre » pour les travaux
Séquestre d'intérêts (échéancier) sequestre (interestSequestre) escrow (legacy : colonne DB totalFundEscrow) ; « séquestre » nu (toujours « séquestre d'intérêts »)
Obligation (titre) obligation bond
Fiducie fiducie (fiducieFees) trust
Caution caution (cautionPersonnelleEtSolidaire) guarantee, surety
Hypothèque hypotheque (hypothequeRang) mortgage
Fiscalité FR prelevements (prelevementsSociaux), suffixes Ht / Ttc

8. Noms de modules

prefix-entity-variant en kebab-case, singulier par défaut. Familles de préfixes : investor-, property-, project-, primary-, lemonway- (déjà réglé). espace-fi- est abandonné. Pluriel uniquement pour les modules registre/collection (wallet-transactions, referrals, property-charts).


Hors périmètre (à trancher plus tard)

Non couvert par ce doc — à traiter dans une passe dédiée : - Forme des réponses & pagination : enveloppe (records / data / total), page / limit / offset / cursor. (data est banni comme identifiant mais présent dans des littéraux de réponse.) - Mécanisme d'enum : discriminatedUnion (idtlt) vs createEnum (space-lift) vs union TS brute — quel choix quand. - Tests (*.test.ts) : application ou non des génériques bannis (result / value). - Enforcement lint : quelles règles automatiser (préfixes booléens, suffixes bannis, casing des statuts).