packages/lib — la frontière des SDK tiers
Règle cardinale : chaque SDK tiers (S3, Mistral, Redis, Auth0, …) vit
derrière un port dans packages/lib. Le code applicatif (apps/web,
apps/api) importe le port, jamais le SDK. Ça sort le lock-in et le churn
des vendeurs hors de la logique métier : changer de fournisseur devient une
édition de config, pas une réécriture.
Anatomie d'une capability
Sous packages/lib/src/<capability>/ :
| Fichier | Rôle | Visibilité |
|---|---|---|
port.ts | Interface(s) + types domaine — la seule surface vue par les clients | exporté |
adapter.<vendor>.ts | Implémentation concrète avec le SDK. Un fichier par vendeur. | jamais ré-exporté |
index.ts | Factory create<Capability>(config) ; switch sur config.driver | exporté |
errors.ts (option) | Erreurs normalisées traversant la frontière | exporté |
Les capabilities actuelles
auth — opérations Management APIEn plus de la vérification JWT et du lookup de profil, le port auth expose
désormais listUsers({ page, perPage, query }) (annuaire paginé Auth0) et
setUserBlocked({ sub, blocked }) (suspension / réactivation). Les deux
passent par la Management API et réutilisent le token M2M mis en cache.
Voir Authentification pour la signature complète.
Les 7 règles (résumé)
package.json#exportsn'expose que des sous-chemins par capability (@pambe/lib/cache,@pambe/lib/storage, …). Jamais de ré-export d'un module SDK.- Les factories prennent un objet de config par injection — elles ne lisent
pas
process.envelles-mêmes. L'app construit la config depuisgetEnv(). - Les types SDK ne fuient pas : entrées/sorties via des types locaux de
port.ts. - Multi-vendeur ⇒ config en union discriminée sur
driver. Ajouter un vendeur = nouveladapter.<vendor>.ts+ variante ; zéro changement côté client. - Les erreurs traversant la frontière sont normalisées
(
StorageNotFoundError, …), pas les erreurs natives du SDK. - Nouveau SDK ⇒ il arrive avec
port.ts+adapter.*.ts+index.tsdans le même changeset. Pas de ré-export « temporaire ». - Les SDK vendeurs n'apparaissent pas dans
apps/*/package.json. S'ils y sont, la frontière a été contournée.
import { createCache, type CacheConfig } from "@pambe/lib/cache"
const config: CacheConfig =
getEnv().NODE_ENV === "production"
? { driver: "upstash", url, token }
: { driver: "redis", url: "redis://localhost:6379" }
const cache = await createCache(config) // l'app ne voit que le type `Cache`
Changer de vendeur par environnement est une édition de config, jamais de code.