Faire des migrations ou reprises de données d'une base de données à une autre, c'est jamais drôle. Personne n'est vraiment satisfait des différents ETLs du marché qui sont souvent propriétaires, ont des clients lourds peu pratiques à utiliser et des formats de fichiers complètement illisibles en XML tous moches... un peu comme les outils que TopModel a remplacé aujourd'hui pour la modélisation.
La solution retenue, quand le développeur à le choix, est très souvent de tout faire à la main dans son langage de prédilection, parce que c'est quasiment à chaque fois la solution la moins prise de tête et celle qui permet d'avoir la meilleure maîtrise du résultat. Et en général, c'est du code bête, répétitif, juste des tuyaux à brancher entre-eux... tiens tiens, j'ai déjà entendu ça quelque part...
Bref, c'est un boulot pour TopModel.
La solution envisagée serait de pouvoir définir des flux de données comme nouveau type d'objet dans un fichier TMD, qui définissent une (ou plusieurs) source(s), une destination, en référençant des classes du modèle qui utilisent les fonctionnalités classiques d'alias et de mapping de TopModel. Un nouveau générateur "ETL" pourra ensuite générer un projet C# complet (on le fera en C# parce que c'est moi qui vais le faire, mais en soit on pourrait aussi le faire en Java ou autre), qui exécutera l'ensemble des flux de données d'un simple dotnet run. L'idée initiale est de ne pas à avoir à modifier le projet généré, puisqu'il s'agira de code généré comme les autres générateurs, mais on imagine très vite que ce que pourra faire TopModel sera forcément limité et que pouvoir coder manuellement certaines transformations ou actions deviendra nécessaire.
La config du générateur devra définir des sources et des destinations pour les données, avec un type (le MVP ne gèrera que PostgreSQL et SQL Server, mais on pourrait imaginer étendre le concept à d'autres SGBDs et des fichiers CSV/Excel/plats...), qui pourront être référencées ensuite dans les flux de données. Potentiellement, il sera possible de spécifier une source/destination de données par défaut, par tag de classe ou non (histoire de ne pas avoir à dire à chaque fois qu'il faut insérer les données dans la même base si c'est toujours le cas). #239 sera nécessaire également pour spécifier les implémentations de domaines à utiliser par source/destination, ainsi que le code C# généré (utiliser sqlType pour les sources/destinations et csharp pour le C# serait acceptable dans un premier temps).
Un flux de données définira une ou plusieurs sources, représentées par des classes du modèle. Il sera possible de renseigner plusieurs sources par classe (ce qui fera une union), ainsi que plusieurs classes, dans quel cas il faudra définir la ou les propriétés sur lesquelles il faudra faire la jointure (et son type). Chaque source pourra optionnellement définir une requête SQL à mapper dans la classe, sinon un simple select * from classe sera utilisé.
Un flux de données définira une destination, également représentée par une classe du modèle. S'il n'y a pas qu'une seule classe dans les sources ou que ce n'est pas la même classe que la destination, alors il devra exister un mapper from sur la classe de destination depuis l'ensemble des classes spécifiées dans la source. Une destination aura aussi un type, qui sera soit replace (pour vider la table et insérer toutes les données), ou bien upsert, pour insérer les données manquantes et mettre à jour les autres. upsert utilisera la clé primaire de la classe de destination et la clé primaire d'une des classes sources (qui devra être précisée s'il y'en a plusieurs).
Un flux de données pourra spécifier des dépendances vers d'autres flux, afin de pouvoir ordonnancer les différents flux les uns avec les autres si nécessaire. Le générateur se débrouillera pour créer l'arbre de dépendances et génèrera le code nécessaire pour l'exécuter dans l'ordre.
Enfin, il sera possible de créer des scripts en plus des flux de données, qui serviront à exécuter un script SQL simple dans une destination et qui pourront être ordonnancés de la même façon.
ça pourrait ressembler à ça :
---
dataFlux:
name: UpsertUtilisateur
source:
- class: Utilisateur
sources:
- name: db1
destination:
class: DimUtilisateur # Il faut un mapper from Utilisateur sur cette classe
name: db2
type: upsert
---
dataFlux:
name: InsertStructure
dependsOn:
- UpsertUtilisateur
source:
- class: DimStructure
sources:
- name: db0
query: "select * from structure"
- name: db1
query: "select * from structure"
destination:
class: DimStructure
name: db2
type: replace
---
script:
dependsOn:
- InsertStructure
name: db2
query: "update structure set str_date_modification = now()"
---
dataFlux:
name: InsertProjet
sources:
- class: Projet
joinColumns: [Id]
sources:
- name: db1
- class: Dossier
joinColumns: [ProjetId]
sources:
- name: db1
joinType: left
destination:
class: DimProjet # Il faut un mapper from Projet + Dossier sur cette classe
name: db2
type: replace
On développera un socle d'ETL TopModel (en C#) qui sera référencé et utilisé par le code généré, qu'on publiera sur nuget et qu'on mettra dans le csproj du projet généré. Il conviendra bien entendu que le code généré et le socle soient performants, gèrent correctement les erreurs, fournissent des logs clairs... On ne va pas chercher à réinventer la roue autant que possible, donc si on a des solutions toutes faites pour certains de nos problèmes dans des librairies .NET existantes, on va les prendre. Pour rappel, l'objectif est d'avoir à ne rien avoir à modifier dans le code généré et de n'avoir à fournir qu'un fichier de configuration pour les connexions (serveur, nom des bases, login/mot de passe...) pour le lancer. Il ne faut pas que le fait qu'on génère un projet C# soit un frein à l'adoption du générateur, il faut bien faire un choix, c'est le plus logique pour nous, et ça reste une solution beaucoup plus accessible que de générer une boîte noire ou de tout faire sans génération.
Faire des migrations ou reprises de données d'une base de données à une autre, c'est jamais drôle. Personne n'est vraiment satisfait des différents ETLs du marché qui sont souvent propriétaires, ont des clients lourds peu pratiques à utiliser et des formats de fichiers complètement illisibles en XML tous moches... un peu comme les outils que TopModel a remplacé aujourd'hui pour la modélisation.
La solution retenue, quand le développeur à le choix, est très souvent de tout faire à la main dans son langage de prédilection, parce que c'est quasiment à chaque fois la solution la moins prise de tête et celle qui permet d'avoir la meilleure maîtrise du résultat. Et en général, c'est du code bête, répétitif, juste des tuyaux à brancher entre-eux... tiens tiens, j'ai déjà entendu ça quelque part...
Bref, c'est un boulot pour TopModel.
La solution envisagée serait de pouvoir définir des flux de données comme nouveau type d'objet dans un fichier TMD, qui définissent une (ou plusieurs) source(s), une destination, en référençant des classes du modèle qui utilisent les fonctionnalités classiques d'alias et de mapping de TopModel. Un nouveau générateur "ETL" pourra ensuite générer un projet C# complet (on le fera en C# parce que c'est moi qui vais le faire, mais en soit on pourrait aussi le faire en Java ou autre), qui exécutera l'ensemble des flux de données d'un simple
dotnet run
. L'idée initiale est de ne pas à avoir à modifier le projet généré, puisqu'il s'agira de code généré comme les autres générateurs, mais on imagine très vite que ce que pourra faire TopModel sera forcément limité et que pouvoir coder manuellement certaines transformations ou actions deviendra nécessaire.La config du générateur devra définir des sources et des destinations pour les données, avec un type (le MVP ne gèrera que PostgreSQL et SQL Server, mais on pourrait imaginer étendre le concept à d'autres SGBDs et des fichiers CSV/Excel/plats...), qui pourront être référencées ensuite dans les flux de données. Potentiellement, il sera possible de spécifier une source/destination de données par défaut, par tag de classe ou non (histoire de ne pas avoir à dire à chaque fois qu'il faut insérer les données dans la même base si c'est toujours le cas). #239 sera nécessaire également pour spécifier les implémentations de domaines à utiliser par source/destination, ainsi que le code C# généré (utiliser
sqlType
pour les sources/destinations etcsharp
pour le C# serait acceptable dans un premier temps).Un flux de données définira une ou plusieurs sources, représentées par des classes du modèle. Il sera possible de renseigner plusieurs sources par classe (ce qui fera une union), ainsi que plusieurs classes, dans quel cas il faudra définir la ou les propriétés sur lesquelles il faudra faire la jointure (et son type). Chaque source pourra optionnellement définir une requête SQL à mapper dans la classe, sinon un simple
select * from classe
sera utilisé.Un flux de données définira une destination, également représentée par une classe du modèle. S'il n'y a pas qu'une seule classe dans les sources ou que ce n'est pas la même classe que la destination, alors il devra exister un mapper
from
sur la classe de destination depuis l'ensemble des classes spécifiées dans la source. Une destination aura aussi un type, qui sera soitreplace
(pour vider la table et insérer toutes les données), ou bienupsert
, pour insérer les données manquantes et mettre à jour les autres.upsert
utilisera la clé primaire de la classe de destination et la clé primaire d'une des classes sources (qui devra être précisée s'il y'en a plusieurs).Un flux de données pourra spécifier des dépendances vers d'autres flux, afin de pouvoir ordonnancer les différents flux les uns avec les autres si nécessaire. Le générateur se débrouillera pour créer l'arbre de dépendances et génèrera le code nécessaire pour l'exécuter dans l'ordre.
Enfin, il sera possible de créer des scripts en plus des flux de données, qui serviront à exécuter un script SQL simple dans une destination et qui pourront être ordonnancés de la même façon.
ça pourrait ressembler à ça :
On développera un socle d'ETL TopModel (en C#) qui sera référencé et utilisé par le code généré, qu'on publiera sur nuget et qu'on mettra dans le csproj du projet généré. Il conviendra bien entendu que le code généré et le socle soient performants, gèrent correctement les erreurs, fournissent des logs clairs... On ne va pas chercher à réinventer la roue autant que possible, donc si on a des solutions toutes faites pour certains de nos problèmes dans des librairies .NET existantes, on va les prendre. Pour rappel, l'objectif est d'avoir à ne rien avoir à modifier dans le code généré et de n'avoir à fournir qu'un fichier de configuration pour les connexions (serveur, nom des bases, login/mot de passe...) pour le lancer. Il ne faut pas que le fait qu'on génère un projet C# soit un frein à l'adoption du générateur, il faut bien faire un choix, c'est le plus logique pour nous, et ça reste une solution beaucoup plus accessible que de générer une boîte noire ou de tout faire sans génération.
YOLO.