mirror of
https://github.com/MaksTinyWorkshop/_Assistant_Lead_Tech
synced 2026-04-06 21:41:42 +02:00
Refonte Structure
This commit is contained in:
188
knowledge/backend/patterns/multi-tenant.md
Normal file
188
knowledge/backend/patterns/multi-tenant.md
Normal file
@@ -0,0 +1,188 @@
|
||||
# Backend — Patterns : Multi-tenant
|
||||
|
||||
> Extrait de la base de connaissance Lead_tech. Voir `knowledge/backend/patterns/README.md` pour l'index complet.
|
||||
|
||||
---
|
||||
|
||||
<a id="pattern-guardrails-multi-tenant-403-404"></a>
|
||||
## Pattern : Guardrails multi-tenant — 403 vs 404 selon la sémantique
|
||||
|
||||
- Objectif : éviter les fuites d'information inter-tenant tout en gardant une sémantique d'erreur claire.
|
||||
- Contexte : API multi-tenant avec ressources métier isolées et surfaces internes ou opérateur.
|
||||
- Quand l'utiliser : dès qu'une vérification d'appartenance tenant peut soit refuser explicitement l'accès, soit masquer l'existence d'une ressource.
|
||||
- Quand l'éviter : contexte mono-tenant ou endpoints purement internes sans enjeu de fuite.
|
||||
- Avantage :
|
||||
- clarifie la convention de sécurité
|
||||
- évite les réponses incohérentes selon les modules
|
||||
- facilite les tests d'isolation tenant
|
||||
- Limites / vigilance :
|
||||
- la convention doit être documentée et appliquée partout
|
||||
- un mauvais choix entre 403 et 404 peut révéler une information sensible
|
||||
- Validé le : 16-03-2026
|
||||
- Contexte technique : API multi-tenant / HTTP / services métier
|
||||
|
||||
### Implémentation (exemple minimal)
|
||||
|
||||
```txt
|
||||
- `assertTenantMatch(actor, expectedTenantId)` -> 403 quand la ressource est connue mais l'accès refusé
|
||||
- `assertResourceBelongsToTenant(actor, resourceTenantId)` -> 404 quand il faut masquer l'existence d'une ressource d'un autre tenant
|
||||
- documenter la convention dans le module
|
||||
- couvrir les deux sémantiques par des tests dédiés
|
||||
```
|
||||
|
||||
### Checklist
|
||||
|
||||
- Convention 403 vs 404 documentée
|
||||
- Helpers distincts selon la sémantique métier
|
||||
- Aucune fuite d'existence cross-tenant sur les ressources métier
|
||||
- Tests dédiés sur les deux comportements
|
||||
|
||||
---
|
||||
|
||||
<a id="pattern-repository-tenant-aware"></a>
|
||||
## Pattern : Repository tenant-aware — `tenantId` obligatoire dans la signature
|
||||
|
||||
- Objectif : rendre impossible par construction une query non scopée sur un domaine multi-tenant.
|
||||
- Contexte : repositories ou services d'accès aux données sur ressources tenant-scoped.
|
||||
- Quand l'utiliser : dès qu'un domaine métier est massivement filtré par tenant.
|
||||
- Quand l'éviter : domaines réellement globaux ou méthodes volontairement cross-tenant.
|
||||
- Avantage :
|
||||
- force le scoping dès la signature TypeScript
|
||||
- réduit les oublis de filtre tenant dans les call sites
|
||||
- rend les exceptions cross-tenant visibles
|
||||
- Limites / vigilance :
|
||||
- les exceptions cross-tenant doivent être rares et documentées explicitement
|
||||
- ne dispense pas d'un second garde-fou dans les mutations sensibles
|
||||
- Validé le : 16-03-2026
|
||||
- Contexte technique : TypeScript / Prisma / architecture repository
|
||||
|
||||
### Implémentation (exemple minimal)
|
||||
|
||||
```txt
|
||||
- chaque méthode métier tenant-scoped prend `tenantId` en paramètre obligatoire
|
||||
- les méthodes réellement cross-tenant sont nommées et documentées comme exception
|
||||
- les call sites Prisma directs sur ces domaines sont interdits ou supprimés
|
||||
```
|
||||
|
||||
### Checklist
|
||||
|
||||
- `tenantId` obligatoire sur les méthodes tenant-scoped
|
||||
- Exceptions cross-tenant documentées
|
||||
- Appels directs concurrents à Prisma supprimés
|
||||
- Tests sur scoping tenant au niveau repository
|
||||
|
||||
---
|
||||
|
||||
<a id="pattern-tenantid-dans-updates"></a>
|
||||
## Pattern : Défense en profondeur — inclure `tenantId` dans les updates
|
||||
|
||||
- Objectif : éviter une mutation cross-tenant même si un identifiant a été mal résolu en amont.
|
||||
- Contexte : `update` ou `updateMany` sur une ressource tenant-scoped.
|
||||
- Quand l'utiliser : dès qu'une mutation dépend d'un `id` reçu ou résolu dans un flux multi-tenant.
|
||||
- Quand l'éviter : ressources globales non liées à un tenant.
|
||||
- Avantage :
|
||||
- ajoute une seconde barrière côté base
|
||||
- réduit l'impact d'un call site mal scopé
|
||||
- rend la mutation plus sûre sans complexité forte
|
||||
- Limites / vigilance :
|
||||
- ne remplace pas le scoping en lecture ni la vérification d'autorisation
|
||||
- suppose que `tenantId` soit disponible au moment de la mutation
|
||||
- Validé le : 16-03-2026
|
||||
- Contexte technique : Prisma / multi-tenant / mutations métier
|
||||
|
||||
### Implémentation (exemple minimal)
|
||||
|
||||
```txt
|
||||
- préférer `where: { id, tenantId }` à `where: { id }` sur les updates tenant-scoped
|
||||
- appliquer la même règle sur `updateMany` et opérations de révocation
|
||||
- conserver les vérifications métier amont, mais ne pas leur déléguer toute la sécurité
|
||||
```
|
||||
|
||||
### Checklist
|
||||
|
||||
- `tenantId` présent dans les clauses `where` des updates sensibles
|
||||
- Pas de mutation tenant-scoped basée sur `id` seul
|
||||
- Revue explicite des exceptions documentées
|
||||
|
||||
---
|
||||
|
||||
<a id="pattern-helper-tenant-module-partage"></a>
|
||||
## Pattern : Extraire les helpers de résolution tenant dans un module partagé dédié
|
||||
|
||||
- Objectif : éviter les couplages sémantiques incorrects entre domaines en centralisant les utilitaires transverses tenant.
|
||||
- Contexte : toute fonction de résolution de tenant utilisée par plusieurs domaines métier.
|
||||
- Quand l'utiliser : dès qu'un helper est importé par plus d'un module métier.
|
||||
- Risque si ignoré : un module métier devient dépendance implicite d'un autre domaine distinct.
|
||||
- Validé le : 17-03-2026
|
||||
- Contexte technique : Next.js / TypeScript — app-template-resto
|
||||
|
||||
### Implémentation
|
||||
|
||||
```typescript
|
||||
// ✅ src/server/tenant/resolvePublicTenant.ts
|
||||
export function resolvePublicTenantSelection(env: NodeJS.ProcessEnv) { ... }
|
||||
|
||||
// ✅ Rétrocompatibilité depuis l'ancien emplacement si nécessaire
|
||||
export { resolvePublicTenantSelection } from "@/server/tenant/resolvePublicTenant";
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
<a id="pattern-helper-feature-flag-tenant"></a>
|
||||
## Pattern : Helper centralisé d'activation de features tenant-scoped
|
||||
|
||||
- Objectif : centraliser la logique d'activation/désactivation de pages ou modules par tenant dans un helper pur.
|
||||
- Contexte : app multi-tenant avec features activables (pages publiques, modules optionnels, intégrations).
|
||||
- Quand l'utiliser : dès qu'une feature peut être activée/désactivée par tenant.
|
||||
- Avantage :
|
||||
- helper pur et testable sans I/O
|
||||
- comportement par défaut sain (`null`/`undefined` → tout activé)
|
||||
- composants de navigation et pages importent ce helper, jamais Prisma directement
|
||||
- Validé le : 17-03-2026
|
||||
- Contexte technique : Next.js App Router / TypeScript — app-template-resto
|
||||
|
||||
### Implémentation
|
||||
|
||||
```typescript
|
||||
// src/server/public/publicPagesConfig.ts
|
||||
export function isPublicPageEnabled(
|
||||
config: PublicPagesConfigRecord | null | undefined,
|
||||
pageKey: PublicPageKey
|
||||
): boolean {
|
||||
if (!config) return true; // config absente = tout activé par défaut
|
||||
return config[PAGE_KEY_TO_CONFIG_FIELD[pageKey]];
|
||||
}
|
||||
```
|
||||
|
||||
**Règle :** `null`/`undefined` → tout activé. Évite les régressions si la config n'a pas été provisionnée.
|
||||
|
||||
---
|
||||
|
||||
<a id="pattern-en-enforcement-tenant"></a>
|
||||
## Pattern : EN enforcement optionnel par tenant (toggle + publish gate)
|
||||
|
||||
- Objectif : permettre à un tenant d'activer l'obligation de remplir les champs traduits EN, avec une gate à la publication.
|
||||
- Contexte : app multi-tenant avec internationalisation optionnelle.
|
||||
- Quand l'utiliser : dès qu'un tenant peut choisir d'activer/désactiver une exigence de contenu i18n.
|
||||
- Validé le : 21-03-2026
|
||||
- Contexte technique : Prisma / Next.js App Router — app-template-resto
|
||||
|
||||
### Implémentation
|
||||
|
||||
```typescript
|
||||
// 1. Modèle Tenant
|
||||
enableEn Boolean @default(false)
|
||||
|
||||
// 2. Vérification dans chaque action mutante (create/update)
|
||||
const { enableEn } = await getEnConfig(tenantId);
|
||||
if (enableEn && !labelEn) throw new HttpError("Traduction EN requise.", { status: 400 });
|
||||
|
||||
// 3. Gate publish — vérification de complétude
|
||||
const result = await checkEnCompleteness(tenantId); // 4 requêtes en Promise.all
|
||||
// Exclut : isSystem:true, tenantId:null, isVisible:false
|
||||
if (!result.complete) throw new HttpError("Contenu EN incomplet.", { status: 422 });
|
||||
```
|
||||
|
||||
**Règles :**
|
||||
- `isVisible: false` n'est pas inclus dans le check (une entité masquée ne bloque pas la publication)
|
||||
- `revalidatePath` sur **toutes** les pages menu après toggle du flag (pas seulement `/settings`)
|
||||
Reference in New Issue
Block a user