La documentation officielle de NestJS explique très clairement comment se connecter à plusieurs bases de données en utilisant une configuration statique.
L’un de nos clients a un cas d’usage différent et souhaite que son application puisse se connecter à plusieurs bases de données chacune étant dédiée à un pays. Il veut pouvoir ajouter et supprimer des bases de données dynamiquement, sans avoir à modifier le code source.
Voici les principales différences entre ces deux modèles :
Statique | Dynamique | |
Structure de la base | Identique ou différente | Doit être identique |
Ajouter/supprimer une base | Modification du code | Modificaton de la configuration et redémarrage de l’application |
Mettre en place la configuration dynamique
Voici le fichier.env
tel qu’on souhaiterait l’utiliser :
DATABASE_SYSTEM_IDS est une liste séparée par des virgules d’identifiants système définis arbitrairement et qui représentent chacune des bases de données à laquelle on souhaite se connecter. Pour chaque identifiant système on définit un jeu de variables nommées DB_xx_* où xx prend la valeur de cet identifiant.
Ci-dessous la classe de configuration qui nous permet de gérer ce genre de configuration (fichier src/common/config/orm.config.ts) :
Voici comment on génère dynamiquement une configuration TypeORM pour chaque base de données (fichier src/home/jnesis/www/wp.module.ts) :
Notez que chacune de ces configurations TypeORM se voit assigner le nom database-xx où xx est l’identifiant système.
Mettre en place l'injection de dépendance dynamique
Maintenant que notre configuration est dynamique, nous avons besoin de nous connecter à une base de données en particulier en utilisant son identifiant système. L’injection de dépendance standard (à travers @InjectEntityManager , @InjectRepository, etc…) ne peut pas être utilisée car elle requiert de connaître la base de données à laquelle on souhaite se connecter au moment de la compilation.
Pour pouvoir sélectionner dynamiquement une base de données pendant l’exécution de l’application, il nous faut utiliser la fonctionnalité module reference de NestJS. De cette manière il est possible d’instancier un EntityManager depuis son nom de configuration TypeORM database-xx (méthode loadEntityManager dans le fichier src/car-manufacturers/car-manufacturers.service.ts) :
Projet de démonstration
Vous trouverez un exemple complet et fonctionnel ici sur Github.
La limitation de structure identique
En réalité nous pourrions avoir des structures différentes même avec des bases de données dynamiques. Cela nécessite de définir la forme de chaque base en indiquant le jeu d’entitées qu’elle va utiliser.
Par exemple nous pourrions ajouter un paramètre au fichier .env :
A partir de là il nous suffit de charger les entitées dynamiquement en se basant sur le nom de la structure en tant que suffixe (fichier src/common/config/orm.config.ts) :
Il faudra alors définir les entités pour chaque structure et elle peuvent être complètement différentes :
- StructureA: car-manufacturer.structureA.entity.ts
- StructureB: brand.structureB.entity.ts, address.structureB.entity.ts
Évidemment les requêtes vont être différentes d’une structure à l’autre, cela nécessitera donc des ajouts de conditions et branchements dans le code métier.