ServicesÀ proposNotesContact Me contacter →
EN FR
Note

Développement contract-first dans dbt

Définir le contrat avant d'écrire le SQL — l'analogie avec la conception d'API, le workflow, et comment ODCS + Data Contract CLI peuvent générer le YAML des modèles dbt.

Planté
dbtdata engineeringdata qualitydata modeling

La plupart des équipes adoptent les contrats de modèles dbt en les appliquant rétrospectivement aux modèles existants : le modèle existe, et quelqu’un écrit le YAML pour le décrire. Une approche alternative pour les nouveaux modèles destinés à servir des consommateurs en aval est de définir le contrat avant d’écrire du SQL.

L’analogie avec la conception d’API

Les ingénieurs logiciels ne construisent pas un endpoint d’API et ne déterminent ensuite ce que devrait être le schéma de réponse. Ils définissent l’interface — quels champs la réponse contiendra, quels types ils auront, quelles contraintes s’appliquent — s’accordent avec les clients qui la consommeront, puis l’implémentent. La définition de l’interface vient en premier ; l’implémentation suit.

Les modèles de données peuvent fonctionner de la même façon. La question n’est pas “qu’est-ce que mon SQL produit ?” — c’est “de quoi les consommateurs ont-ils besoin ?” Définir le contrat en premier signifie commencer par cette question et faire en sorte que le SQL satisfasse la réponse plutôt que l’inverse.

Le workflow contract-first

Étape 1 : S’accorder sur l’interface. C’est une conversation entre l’équipe qui construit le modèle et les équipes qui le consomment. Quelles colonnes le consommateur a-t-il besoin ? Quels types ? Quelles contraintes ? Cela se passe avant qu’un code soit écrit. Le résultat est un accord, pas du YAML.

Étape 2 : Écrire le contrat YAML. Traduire l’accord en une définition de modèle dbt avec contract: {enforced: true}, des déclarations de colonnes avec les types, et toutes les contraintes :

models:
- name: mrt__analytics__customers
access: public
config:
materialized: table
contract:
enforced: true
columns:
- name: customer__id
data_type: integer
constraints:
- type: not_null
- type: primary_key
- name: customer__name
data_type: text
- name: customer__lifetime_value
data_type: numeric(38,2)
- name: customer__segment
data_type: text
- name: customer__is_active
data_type: boolean

Étape 3 : Implémenter le modèle. Écrire le SQL qui satisfait le contrat. Si votre requête retourne une colonne avec le mauvais type, ou retourne des colonnes que le YAML ne déclare pas, dbt compile vous le dit immédiatement :

Compilation Error in model mrt__analytics__customers
This model has an enforced contract that failed.
| column_name | definition_type | contract_type | mismatch_reason |
| ------------------------ | --------------- | ------------- | ------------------ |
| customer__lifetime_value | TEXT | NUMERIC(38,2) | data type mismatch |

L’étape de compilation est la boucle de rétroaction. Vous ne lancez pas le build complet et ne vérifiez pas les sorties de tests — vous compilez, vous voyez exactement ce qui ne correspond pas, et vous le corrigez. La CI valide le reste.

Étape 4 : Marquer le contrat comme l’interface canonique. Une fois que le modèle se construit proprement, définissez access: public et communiquez la version aux consommateurs. Ils peuvent commencer à référencer ref('your_project', 'mrt__analytics__customers') en sachant que le contrat garantit la forme.

Pourquoi cet ordre est important

L’approche de rétro-application — écrire le modèle, ajouter le contrat pour le décrire — traite le contrat comme de la documentation de ce qui existe déjà. Le contrat reflète les décisions d’implémentation prises sans apport des consommateurs.

Le contract-first inverse la dépendance. Les consommateurs expriment leurs besoins. Le contrat capture ces besoins. Le SQL existe pour satisfaire le contrat. C’est une différence significative en pratique :

  • Les colonnes dont les consommateurs n’ont pas besoin ne se retrouvent pas dans le contrat, ce qui garde les marts ciblés
  • Les décisions de type sont prises consciemment plutôt que de prendre par défaut ce que SQL retourne
  • Les changements breaking nécessitent une renégociation explicite, parce que le contrat était l’accord original
  • Plusieurs consommateurs en aval peuvent réviser le YAML proposé et signaler des problèmes avant que l’implémentation commence

Le coût de friction est une coordination initiale, qui semble plus lente. La contrepartie est que l’interface du modèle est délibérément conçue plutôt qu’accidentellement documentée.

Intégration ODCS + Data Contract CLI

L’Open Data Contract Standard (ODCS) va encore plus loin. Vous pouvez définir un contrat en utilisant la spécification YAML d’ODCS — qui capture les SLA, les métadonnées de propriété et les règles de qualité au-delà de ce que dbt gère — puis générer le YAML du modèle dbt à partir de celui-ci en utilisant le Data Contract CLI :

Terminal window
datacontract export --format dbt

Le YAML dbt généré est un sous-ensemble du contrat plus large, concentré sur les garanties structurelles que dbt peut appliquer au moment de la compilation. Le contrat ODCS plus large vit comme un document autonome couvrant l’accord complet sur le data product : qui en est propriétaire, quels SLA s’appliquent, quelles règles de qualité sont intégrées.

Cela fait le pont entre deux niveaux de contrat :

  • Niveau organisationnel : le contrat ODCS, convenu entre producteurs et consommateurs, couvrant l’ensemble du cycle de vie du data product
  • Niveau technique : le contrat de modèle dbt, appliqué au moment de la compilation, couvrant les garanties structurelles

Le workflow : définissez le contrat ODCS (avec ou sans outillage, un fichier YAML suffit), exécutez datacontract export --format dbt pour générer les déclarations de colonnes initiales, puis ajoutez le SQL d’implémentation. Les changements apportés au contrat organisationnel peuvent être ré-exportés pour maintenir le YAML dbt à jour.

Pour les équipes qui n’ont pas encore besoin des métadonnées organisationnelles d’ODCS, l’approche contract-first ne le nécessite pas. Vous écrivez simplement le YAML vous-même, vous vous accordez avec les consommateurs avant d’écrire le SQL, et vous utilisez dbt compile comme boucle de rétroaction d’implémentation.

Point de départ pratique

L’approche contract-first est la plus précieuse pour :

  • Les nouveaux modèles qui seront access: public
  • Les modèles dans une configuration dbt Mesh où des consommateurs cross-projet existent dès le premier jour
  • Les tables à enjeux élevés (tableaux de bord exécutifs, flux de données partenaires) où la stabilité de l’interface est immédiatement importante

Pour les modèles internes dont personne d’autre ne dépend, la surcharge d’une conception formelle contract-first n’est pas justifiée. L’investissement en gouvernance s’adapte au nombre de consommateurs qui dépendent de l’interface.

La stratégie de déploiement pour les modèles existants décrit comment appliquer des contrats sur des modèles déjà construits. Le contract-first est la version prospective de ce travail — l’approche qui rend le rétro-application inutile.