Mettre à jour le modèle de données
Vue d’ensemble
Lors du cycle de développement d'un parcours, le modèle de données du projet est amené à évoluer selon les besoins. Le cas courant est par exemple l'ajout d'une nouvelle entité.
Le framework OIP offre une assistance via des commandes pour faciliter et automatiser la migration du modèle de données d'un projet, depuis l'évolution du fichier de configuration des entités jusqu'à la mise à niveau du schéma du modèle en base de données.
Vous trouverez ci-après un cas pratique complet et l'explication des différentes étapes nécessaires pour mettre à jour le modèle de données de votre projet.
Prérequis
Ce guide s'applique pour un projet déjà initialisé.
Il requiert :
☑️ Le fichier de configuration des entités au format Open API ou MJS, défini dans le dossier config/entities à la racine ;
☑️ La commande de génération des fichiers des entités entities:gen, définie dans le fichier package.json :
{
"scripts": {
"entities:gen": "oip-tools entities generate --oas-config",
}
}☑️ La dépendance node-pg-migrate installée :
{
"dependencies": {
"node-pg-migrate": "8.0.4"
}
}☑️ Le script d'exécution des fichiers de migration run-migrations.sh, défini dans le dossier scripts à la racine du projet :
#!/bin/sh
DATABASE_URL=postgres://postgres:postgres@localhost:5432/<oav-database>
if [ -n "$1" ]; then
DATABASE_URL=$1
fi
DATABASE_URL=$DATABASE_URL PGSSLMODE=disable node node_modules/.bin/node-pg-migrate up --migrations-table data_migrations
☑️ La commande d'exécution des migrations migrate:dev, définie dans le fichier package.json :
{
"scripts": {
"migrate:dev": "./scripts/run-migrations.sh"
}
}Cas pratique
Les étapes suivantes s'appuient sur un fichier de configuration des entités au format Open API. Si vous travaillez avec un fichier au format MJS (legacy), les étapes sont identiques.
État initial du modèle de données
Prenons comme exemple le fichier de configuration initial suivant :
openapi: 3.1.2
info:
title: Magic Project
version: 4.0.0-alpha.0
contact:
email: [email protected]
components:
schemas:
# Modélisation de l'entité Project
Project:
type: object
x-fasst-entity-category: OIP_ENTITY_ROOT
description: L'entité Project est l'entité principale qui porte l'ensemble des autres entités
required: ['name', 'externalId']
properties:
name:
description: Nom du projet client
type: string
externalId:
description: Identifiant externe du projet
type: string
companies:
description: liste des entreprises associées au projet client
type: array
readOnly: false
items:
$ref: "#/components/schemas/Company"
customers:
description: liste des clients associées au projet client
type: array
readOnly: false
items:
$ref: "#/components/schemas/Customer"
# Modélisation de l'entité Company
Company:
x-fasst-entity-category: OIP_ENTITY
description: L'entité Company contient les informations de l'entreprise
type : object
properties:
name:
description: Nom de l'entreprise
type: string
siret:
description: Numéro SIRET de l'entreprise
type: string
# Modélisation de l'entité Customer
Customer:
x-fasst-entity-category: OIP_ENTITY
description: L'entité Customer contient les informations du client
type : object
properties:
firstName:
description: Prénom du client
type: string
lastName:
description: Nom du client
type: stringCe fichier nous a été fournit par l'architecte responsable de notre projet. Il est placé dans le dossier config/entities à la racine :
Project Root/
└── config/
└── entities/
└── entities.openapi.ymlIl définit 3 entités et leurs relations :
- une entité Project (l'entité racine) ;
- une entité Company ;
- une entité Customer ;
- une relation Project -> Company ;
- une relation Project -> Customer.
Ces entités et leurs relations sont l'état actuel du schéma de notre modèle en base de données.
Inspection des types d'entités actuelles en base de données :
$ SELECT * FROM public.entities_types ORDER BY id ASC;
id | type | is_root | data_encrypted | application_id
----+----------+---------+----------------+----------------
1 | Project | t | {} | 1
3 | Company | f | {} | 1
4 | Customer | f | {} | 1
(3 rows)Inspection des relations actuelles en base de données :
$ SELECT * FROM public.entities_relations_types ORDER BY src_type_id ASC, tgt_type_id ASC;
src_type_id | tgt_type_id
-------------+-------------
1 | 3
1 | 4
(2 rows)Mise à jour du fichier de configuration des entités
L'architecte responsable de notre projet nous indique qu'il a ajouté une nouvelle entité Contact au fichier de configuration des entités.
Il nous demande alors de mettre à jour le modèle de données du projet pour pouvoir utiliser l'entité Contact dans le code de notre OAV.
Voici le fichier de configuration des entités mis à jour par l'architecte :
openapi: 3.1.2
info:
# ...
components:
schemas:
# Modélisation de l'entité Project
Project:
# ...
# Modélisation de l'entité Company
Company:
# ...
properties:
# ...
contacts:
$ref: "#/components/schemas/Contact"
# Modélisation de l'entité Customer
Customer:
# ...
properties:
# ...
contacts:
$ref: "#/components/schemas/Contact"
# Modélisation de l'entité Contact
Contact:
x-fasst-entity-category: OIP_ENTITY
description: Bla bla bla
type : object
properties:
email:
description: Email
type: string
phoneNumber:
description: Phone number
type: stringNous remarquons les changements suivants au niveau du schéma du modèle de données :
- l'ajout de l'entité Contact ;
- l'ajout de la relation Company -> Contact ;
- l'ajout de la relation Customer -> Contact.
Le schéma de l'entité Project reste inchangé.
Génération des fichiers liés aux entités et au modèle de données
Pour appliquer ces changements nous allons tout d'abord utiliser la commande entities:gen définie dans le fichier package.json du projet :
{
scripts: {
"entities:gen": "oip-tools entities generate --oas-config"
}
}Cette commande permet de générer, à partir du fichier de configuration des entités config/entities/entities.openapi.yml, les fichiers suivants :
- les fichiers qui exposent les fonctions de manipulation des entités, générés dans le dossier entities ;
- un fichier de migration pour mettre à jour le schéma du modèle en base de données, généré dans le dossier migrations.
Exécutons la commande :
npm run entities:genLes logs en sortie de la commande nous indiquent les entités détectées, les fichiers générés ou les erreurs éventuelles.
Une fois l'exécution de la commande terminée, nous pouvons vérifier que les fichiers suivants ont été générés :
- le fichier de manipulation de l'entité Contact :
Project Root/
└── entities/
└── Contact/
└── index.mjs- un nouveau fichier de migration, son nom est préfixé par un timestamp (qui correspond à l'horodatage de la génération du fichier) :
Project Root/
└── migrations/
└── 1774963667201_migration.jsLe nouveau fichier de migration contient les 3 requêtes SQL nécessaires pour mettre à jour le schéma de notre modèle en base de données :
- la création de l'entité Contact ;
- la création de la relation Company -> Contact ;
- la création de la relation Customer -> Contact.
Mise à jour du schéma du modèle de données
[warn] Ne jamais supprimer ou éditer un fichier de migration (dossier migrations) qui a été exécuté en base de données.La dépendance node-pg-migrate utilisée pour gérer les migrations de données vérifie l'existence des fichiers précédemment exécutés. La commande de migration migrate:dev échouera si un fichier de migration est manquant.
Il nous reste donc une dernière étape à effectuer : exécuter le script de migration pour mettre à jour le schéma interne du modèle dans la base de données.
Notre projet embarque un script run-migrations.sh qui exécute les fichiers de migrations en attente, présents dans le dossier migrations à la racine :
Project Root/
└── scripts/
└── run-migrations.shLa commande migrate:dev, définie dans le fichier package.json de notre projet, appelle le script run-migrations.sh :
{
"scripts": {
"migrate:dev": "./scripts/run-migrations.sh",
}
}Exécutons la commande migrate:dev :
npm run migrate:devLes logs en sortie de la commande nous indiquent quels fichiers de migration ont été exécutés ainsi que le statut final de la migration ou les erreurs éventuelles.
Une fois la migration terminée, nous pouvons vérifier la présence du nouveau type de l'entité Contact en base de données :
$ SELECT * FROM public.entities_types ORDER BY id ASC;
id | type | is_root | data_encrypted | application_id
----+----------+---------+----------------+----------------
1 | Project | t | {} | 1
3 | Company | f | {} | 1
4 | Customer | f | {} | 1
5 | Contact | f | {} | 1
(4 rows)Ainsi que les deux nouvelles relations associées :
$ SELECT * FROM public.entities_relations_types ORDER BY src_type_id ASC, tgt_type_id ASC;
src_type_id | tgt_type_id
-------------+-------------
1 | 3
1 | 4
3 | 5
4 | 5
(4 rows)✅ Nous avons terminer de mettre à jour le modèle de données de notre projet. L'entité Contact est maintenant prête à l'emploi.
Résumé
La mise à jour du modèle de données d'un projet est automatisée et assistée par des commandes.
📌 Les étapes sont les suivantes :
- Mise à jour du fichier de configuration des entités ;
- Exécution de la commande de génération des fichiers des entités entities:gen ;
- Vérification des fichiers générés et notamment de la présence d'un nouveau fichier de migration ;
- Exécution de la commande de migration du modèle de données migrate:dev si nécessaire.
🎯 Point important à retenir : Ne jamais supprimer ou éditer un fichier de migration du dossier migrations, qui a été exécuté en base de données.