ServicesÀ proposNotesContact Me contacter →
EN FR
Note

Comparaison des mécanismes de validation dbt

En quoi les contrats dbt, les tests de données et dbt-expectations diffèrent selon le moment d'exécution, la couverture et le coût — et pourquoi il faut les trois.

Planté
dbtdata qualitytesting

dbt propose trois mécanismes de validation distincts : les contrats, les tests de données et dbt-expectations. Ils s’exécutent à des moments différents du cycle de build, détectent des catégories de problèmes différentes et génèrent des coûts de calcul différents.

Les trois mécanismes

Les contrats s’exécutent à la compilation. Avant qu’un seul SQL n’atteigne l’entrepôt, dbt compare les colonnes que votre modèle retournerait avec ce que vous avez déclaré en YAML. Un écart de noms de colonnes ou de types de données déclenche une erreur de compilation. Le modèle ne se matérialise pas. Le coût de calcul est nul — il s’agit d’une comparaison de métadonnées, pas d’une requête en entrepôt. Le mode d’échec : le modèle n’existe pas encore.

Les tests de données s’exécutent après la construction du modèle. Ils exécutent des requêtes SQL sur la table réelle pour vérifier le contenu : unicité, taux de nulls, intégrité référentielle, plages de valeurs. Un test unique sur customer_id interroge la table construite et échoue si des doublons existent. Le modèle existe déjà quand les tests s’exécutent — ce qui signifie que les modèles en aval ont peut-être déjà été consommés. Le mode d’échec : la table est là, les mauvaises données sont dedans.

dbt-expectations est un package communautaire fournissant plus de 50 tests inspirés de Great Expectations. Comme les tests de données, il s’exécute après le build avec de vraies requêtes SQL — mais il couvre à la fois la validation au niveau du schéma et du contenu. expect_table_columns_to_match_set vérifie qu’une table contient exactement les colonnes spécifiées. expect_column_values_to_be_between valide les plages de valeurs. Et surtout : il fonctionne sur les sources, ce que les contrats ne peuvent pas faire.

AspectContratsTests de donnéesdbt-expectations
Moment d’exécutionCompilationAprès le buildAprès le build
Ce qu’il vérifieNoms de colonnes, typesContenu (valeurs, unicité)Schéma + contenu
Coût de calculNulRequêtes SQLRequêtes SQL
Fonctionne sur les sourcesNonOuiOui
En cas d’échecLe modèle ne se construit pasLe modèle existe, le test échoueLe modèle existe, le test échoue
CouvertureStructurelle uniquementContenu uniquementLes deux

Pourquoi chacun existe

Les modes d’échec expliquent pourquoi les trois mécanismes sont réellement nécessaires plutôt que de miser sur un seul.

Un modèle contractualisé sans tests garantit la forme mais peut livrer des valeurs erronées. Chaque colonne est du bon type, du bon nom — mais si votre colonne status contient désormais des valeurs jamais vues auparavant, le contrat est silencieux. La table se construit. Les données erronées atteignent vos tableaux de bord.

Un modèle bien testé sans contrat peut gagner ou perdre silencieusement des colonnes quand quelqu’un refactorise le SQL. Les tests s’exécutent après le build, donc le nouveau schéma est déjà matérialisé au moment où les tests pourraient détecter le problème. Les modèles en aval ont peut-être déjà fait une sélection dessus. Les contrats empêchent entièrement cette catégorie de ruptures — le modèle refactorisé ne se construit jamais.

dbt-expectations comble les deux lacunes. Il ajoute les vérifications au niveau du schéma que les tests de données ne peuvent pas exprimer (est-ce que cette table contient exactement ces colonnes ? le nombre de colonnes est-il celui attendu ?) et il atteint les sources, où les contrats sont explicitement hors périmètre.

Compilation vs. post-build

L’axe le plus important est le timing. Les contrats sont le seul mécanisme qui empêche un état défaillant d’atteindre l’entrepôt. Tout le reste est une détection après coup.

Cela a des implications concrètes. Quand un test échoue, la table existe déjà. Si vous exécutez dbt build sur un DAG, les modèles en aval ont peut-être déjà été exécutés en utilisant la mauvaise version de l’upstream. Votre job CI détecte l’échec et bloque la fusion, mais le calcul était déjà consommé et les modèles en aval sont maintenant dans un état inconnu.

Quand un contrat échoue, le build s’arrête immédiatement. Rien en aval ne s’exécute. Aucun calcul n’est gaspillé sur des modèles qui auraient eu de mauvaises entrées. Le message d’erreur est suffisamment précis pour agir 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__id | TEXT | INT | data type mismatch |

La contrepartie est la couverture. Les contrats ne valident que la forme — vous ne pouvez pas exprimer une vérification de plage ou une assertion d’unicité dans un contrat. Pour la validation du contenu, on revient aux tests post-build.

La répartition pratique

Le bon modèle mental : les contrats préviennent les ruptures structurelles, les tests de données imposent les règles de contenu, dbt-expectations couvre les lacunes dans les deux directions.

Pour un modèle de mart qui sert des consommateurs en aval, il faut :

  • Un contrat appliqué sur chaque colonne et type (garantie structurelle, zéro calcul)
  • unique + not_null sur les clés primaires (garantie de contenu, requêtes SQL peu coûteuses)
  • dbt_expectations.expect_column_values_to_be_between sur les métriques clés (garantie de règle métier, requêtes SQL peu coûteuses)

Pour une source alimentant ce mart, les contrats ne s’appliquent pas. Utilisez dbt-expectations sur les sources pour approximer la garantie structurelle que les contrats offrent sur les modèles.

En CI/CD, cette superposition signifie que les échecs surgissent à des moments différents. Une violation de contrat arrête le build immédiatement avec une erreur de compilation. Un échec de test surgit après le build, associé à la requête de test spécifique. Les deux sont nécessaires parce qu’ils détectent des catégories différentes de problèmes, et non le même problème avec des outils différents.

Où placer quoi

La taxonomie des tests dbt couvre cela en détail, mais le principe pour ces trois mécanismes :

Contrats : modèles de mart qui servent des consommateurs, en particulier les modèles access: public dans une configuration dbt Mesh. Pas utiles sur les modèles de base ou intermédiaires — ces couches sont internes, et la surcharge des déclarations complètes de colonnes sur 50 modèles intermédiaires n’est pas justifiée.

Tests de données : partout, proportionnellement à la dépendance des consommateurs en aval vis-à-vis du modèle. Chaque clé primaire a besoin de unique + not_null. Les marts critiques nécessitent des vérifications d’intégrité référentielle. Les sources nécessitent des vérifications de fraîcheur.

dbt-expectations : sources (pour les vérifications de schéma qui approximent les contrats) et marts (pour l’application des règles métier). La valeur la plus élevée par test se trouve aux extrémités du DAG — sources où les problèmes entrent et marts où les consommateurs les voient.

Le modèle de qualité à trois couches (contrats, tests, détection d’anomalies) traite l’ensemble de ces mécanismes comme la couche de validation réactive. Elementary se situe dans la troisième couche, détectant les anomalies qu’aucun contrat ni test explicite n’avait anticipées.