GUIDE TECHNIQUE DATA

dbt (Data Build Tool) : Le Guide Complet

Transformer vos données brutes en informations fiables. Maîtrisez le « T » du Modern Data Stack : modélisation, tests et documentation.

Data Guide
15 min
Quality First

Qu'est-ce que dbt ?

dbt (Data Build Tool) est un outil open-source qui permet de transformer les données directement dans votre data warehouse en écrivant du SQL. C'est le « T » (Transform) du pipeline ELT. dbt ne charge pas les données (c'est le rôle d'outils comme Meltano, Fivetran, ou Airbyte) — il les transforme une fois qu'elles sont dans le warehouse.

SQL
Langage principal — pas besoin de Python ou Java pour transformer vos données
T du ELT
Ne fait QUE la transformation — pas l'extraction ni le chargement
Git-native
Tout est versionné : modèles, tests, docs, macros
40K+
Entreprises utilisatrices, de la startup au Fortune 500

Analogie pour débutants

Imaginez une cuisine industrielle. Les ingrédients bruts (données sources) arrivent par camion (extraction). Ils sont stockés dans un immense frigo (data warehouse). dbt, c'est le chef cuisinier : il prend les ingrédients bruts et les transforme en plats finis (tableaux de bord, rapports, métriques) en suivant des recettes (modèles SQL). Et comme un bon chef, il documente ses recettes, teste la qualité des plats, et organise sa cuisine de manière reproductible.

dbt Core vs dbt Cloud

Critère dbt Core (Open-Source) dbt Cloud (SaaS)
Prix Gratuit À partir de $100/mois
Exécution En local ou votre CI/CD Hébergé par dbt Labs
IDE Votre éditeur favori IDE web intégré
Scheduler Cron, Airflow, Dagster, Prefect Intégré
Documentation Générée localement Hébergée automatiquement
CI/CD À configurer vous-même Slim CI intégré
Idéal pour Équipes techniques, open-source Équipes mixtes, onboarding rapide

Vision Globale : Où se Place dbt ?

Le Modern Data Stack

dbt s'inscrit dans le Modern Data Stack, une architecture data modulaire où chaque outil a une responsabilité claire :

Étape Rôle Outils
Extract Récupérer les données des sources Meltano, Fivetran, Airbyte, Stitch
Load Charger dans le warehouse Meltano, Fivetran, Airbyte
Transform Transformer les données brutes en modèles analytiques dbt ⭐
Warehouse Stocker et requêter les données Snowflake, BigQuery, Redshift, DuckDB, Postgres
Orchestrate Planifier et orchestrer les pipelines Airflow, Dagster, Prefect, dbt Cloud
Visualize Créer des dashboards Looker, Metabase, Superset, Evidence
Quality Surveiller la qualité des données dbt tests, Great Expectations, Soda, Elementary

Pourquoi ELT et pas ETL ?

L'ancien paradigme ETL (Extract-Transform-Load) transformait les données avant de les charger, ce qui demandait des outils complexes (Informatica, Talend). Le nouveau paradigme ELT charge d'abord les données brutes, puis les transforme dans le warehouse. Avantage : le warehouse moderne est assez puissant pour faire les transformations, et vous gardez toujours les données brutes.

Concepts Clés de dbt

1. Les Modèles (Models)

Un modèle dbt est simplement un fichier SQL avec une requête SELECT. dbt exécute ce SELECT et matérialise le résultat comme une table ou une vue dans le warehouse.

Exemple : Un modèle simple

-- models/staging/stg_orders.sql
SELECT
    id AS order_id,
    user_id AS customer_id,
    order_date,
    status,
    amount / 100.0 AS amount_eur  -- centimes → euros
FROM {{ source('raw', 'orders') }}
WHERE status != 'cancelled'

Ce modèle prend les commandes brutes, renomme les colonnes, convertit les montants, et filtre les annulées.

2. Les Sources (Sources)

Les sources déclarent les tables brutes qui arrivent dans votre warehouse (via Meltano, Fivetran, etc.). Elles sont déclarées dans un fichier YAML :

# models/staging/_sources.yml
version: 2
sources:
  - name: raw
    database: my_warehouse
    schema: raw_data
    tables:
      - name: orders
        description: "Commandes brutes depuis Shopify"
        loaded_at_field: _loaded_at
        freshness:
          warn_after: { count: 12, period: hour }
          error_after: { count: 24, period: hour }

3. Les Matérialisations

Type Comportement Quand l'utiliser
view Crée une vue SQL (pas de stockage) Modèles légers, staging, données toujours fraîches
table Crée une vraie table (stockage physique) Modèles lourds, agrégations, marts finaux
incremental N'ajoute que les nouvelles lignes Tables volumineuses (logs, événements, transactions)
ephemeral CTE inlinée (pas de table/vue) Sous-requêtes réutilisables, pas de matérialisation

4. Les Tests

dbt intègre des tests de qualité des données directement dans votre pipeline :

# models/staging/_schema.yml
version: 2
models:
  - name: stg_orders
    columns:
      - name: order_id
        tests:
          - unique
          - not_null
      - name: status
        tests:
          - accepted_values:
              values: ['pending', 'shipped', 'delivered', 'returned']
      - name: customer_id
        tests:
          - relationships:
              to: ref('stg_customers')
              field: customer_id

4 tests génériques natifs : unique, not_null, accepted_values, relationships. Vous pouvez aussi écrire des tests SQL custom.

5. Jinja & Macros

dbt utilise Jinja2 comme moteur de template, ce qui rend le SQL dynamique et réutilisable :

-- Référencer un autre modèle
SELECT * FROM {{ ref('stg_orders') }}

-- Référencer une source
SELECT * FROM {{ source('raw', 'orders') }}

-- Condition selon l'environnement
{% if target.name == 'prod' %}
    WHERE created_at >= '2024-01-01'
{% endif %}

-- Boucle dynamique
{% set payment_methods = ['credit_card', 'paypal', 'bank'] %}
{% for method in payment_methods %}
    SUM(CASE WHEN payment_method = '{{ method }}'
        THEN amount ELSE 0 END) AS {{ method }}_amount
    {% if not loop.last %},{% endif %}
{% endfor %}

Architecture en Couches (Le Pattern Fondamental)

La convention la plus importante de dbt est l'architecture en 3 couches :

1. Staging
Nettoyage & Normalisation
1:1 avec les sources. Renommage, cast, filtrage basique. Toujours des vues.
2. Intermediate
Logique Métier
Jointures, agrégations, calculs complexes. Modèles internes, pas exposés aux utilisateurs.
3. Marts
Tables Finales
Prêtes pour la BI / l'analytique. Organisées par domaine métier (finance, marketing, produit).

Structure de Dossiers Recommandée

models/
├── staging/                  # Couche 1 : Nettoyage
│   ├── shopify/
│   │   ├── _shopify__sources.yml
│   │   ├── _shopify__models.yml
│   │   ├── stg_shopify__orders.sql
│   │   └── stg_shopify__customers.sql
│   └── stripe/
│       ├── _stripe__sources.yml
│       ├── stg_stripe__payments.sql
│       └── stg_stripe__refunds.sql
├── intermediate/             # Couche 2 : Logique métier
│   ├── int_orders_with_payments.sql
│   └── int_customer_lifetime.sql
└── marts/                    # Couche 3 : Tables finales
    ├── finance/
    │   ├── fct_revenue.sql         # Fact table
    │   └── dim_customers.sql       # Dimension table
    ├── marketing/
    │   ├── fct_campaigns.sql
    │   └── dim_channels.sql
    └── product/
        └── fct_user_activity.sql

Conventions de Nommage

Préfixe Couche Exemple Règle
stg_ Staging stg_shopify__orders 1:1 avec la source, renommage + cast
int_ Intermediate int_orders_with_payments Jointures, logique métier interne
fct_ Marts (facts) fct_revenue Tables de faits (événements, transactions)
dim_ Marts (dimensions) dim_customers Tables de dimensions (entités, référentiels)

Use Cases Activables

1. E-Commerce : Tableau de Bord des Ventes

Pipeline complet

-- models/marts/finance/fct_daily_revenue.sql
WITH orders AS (
    SELECT * FROM {{ ref('stg_shopify__orders') }}
),
payments AS (
    SELECT * FROM {{ ref('stg_stripe__payments') }}
)
SELECT
    o.order_date,
    COUNT(DISTINCT o.order_id) AS total_orders,
    COUNT(DISTINCT o.customer_id) AS unique_customers,
    SUM(p.amount) AS gross_revenue,
    SUM(p.amount) - SUM(p.fees) AS net_revenue,
    SUM(p.amount) / COUNT(DISTINCT o.order_id) AS avg_order_value
FROM orders o
LEFT JOIN payments p ON o.order_id = p.order_id
GROUP BY o.order_date

Ce modèle alimente directement un dashboard Metabase/Looker avec les KPIs de vente.

2. SaaS : Calcul du MRR et Churn

-- models/marts/finance/fct_mrr.sql
WITH subscriptions AS (
    SELECT * FROM {{ ref('int_active_subscriptions') }}
)
SELECT
    date_trunc('month', period_start) AS month,
    SUM(CASE WHEN type = 'new' THEN mrr ELSE 0 END) AS new_mrr,
    SUM(CASE WHEN type = 'expansion' THEN mrr ELSE 0 END) AS expansion_mrr,
    SUM(CASE WHEN type = 'contraction' THEN mrr ELSE 0 END) AS contraction_mrr,
    SUM(CASE WHEN type = 'churn' THEN mrr ELSE 0 END) AS churned_mrr,
    SUM(mrr) AS total_mrr
FROM subscriptions
GROUP BY 1

3. Marketing : Attribution Multi-Touch

-- models/marts/marketing/fct_attribution.sql
SELECT
    campaign_id,
    channel,
    SUM(first_touch_revenue) AS first_touch_attributed,
    SUM(last_touch_revenue) AS last_touch_attributed,
    SUM(linear_revenue) AS linear_attributed,
    COUNT(DISTINCT customer_id) AS customers_acquired,
    SUM(spend) AS total_spend,
    SUM(linear_revenue) / NULLIF(SUM(spend), 0) AS roas
FROM {{ ref('int_touchpoints_attributed') }}
GROUP BY campaign_id, channel

4. Finance : Rapprochement Bancaire

-- models/intermediate/int_bank_reconciliation.sql
SELECT
    b.transaction_id,
    b.amount AS bank_amount,
    e.amount AS erp_amount,
    ABS(b.amount - e.amount) AS difference,
    CASE
        WHEN e.transaction_id IS NULL THEN 'unmatched_bank'
        WHEN ABS(b.amount - e.amount) > 0.01 THEN 'amount_mismatch'
        ELSE 'matched'
    END AS reconciliation_status
FROM {{ ref('stg_bank__transactions') }} b
FULL OUTER JOIN {{ ref('stg_erp__transactions') }} e
    ON b.transaction_id = e.transaction_id

Cycle de Vie d'un Projet dbt

Phase Commande dbt Ce qui se passe
1. Init dbt init mon_projet Crée la structure de projet avec les dossiers et fichiers de config
2. Développement dbt run --select stg_orders Compile et exécute un modèle spécifique dans le warehouse
3. Test dbt test Exécute tous les tests de qualité des données
4. Documentation dbt docs generate && dbt docs serve Génère et sert la documentation interactive (avec DAG)
5. Build complet dbt build Exécute run + test + snapshot en une seule commande
6. Deploy dbt run --target prod Exécute tous les modèles dans l'environnement de production
7. Freshness dbt source freshness Vérifie que les sources sont à jour (pas de données stale)

Le DAG (Directed Acyclic Graph)

dbt construit automatiquement un graphe de dépendances entre vos modèles grâce aux fonctions ref() et source(). Ce DAG garantit que les modèles sont exécutés dans le bon ordre et permet de ne relancer que ce qui est nécessaire.

Le DAG est votre meilleur ami

Le DAG de dbt est l'un de ses plus grands avantages. Il permet de visualiser toute la chaîne de transformation, d'identifier les dépendances circulaires, et d'exécuter des sous-ensembles du pipeline avec les sélecteurs (dbt run --select +fct_revenue pour exécuter fct_revenue et tous ses ancêtres).

Features Clés

1. Snapshots (Slowly Changing Dimensions)

Les snapshots capturent l'historique des changements dans vos données sources :

-- snapshots/snap_customers.sql
{% snapshot snap_customers %}
{{
    config(
        target_schema='snapshots',
        unique_key='customer_id',
        strategy='timestamp',
        updated_at='updated_at'
    )
}}
SELECT * FROM {{ source('raw', 'customers') }}
{% endsnapshot %}

Chaque fois qu'un client change d'adresse ou de plan, dbt crée une nouvelle ligne avec des colonnes dbt_valid_from et dbt_valid_to.

2. Modèles Incrémentaux

-- models/marts/fct_events.sql
{{
    config(
        materialized='incremental',
        unique_key='event_id',
        incremental_strategy='merge'
    )
}}
SELECT
    event_id,
    user_id,
    event_type,
    created_at
FROM {{ ref('stg_events') }}

{% if is_incremental() %}
    WHERE created_at > (SELECT MAX(created_at) FROM {{ this }})
{% endif %}

Au lieu de recréer la table entière à chaque run (qui peut prendre des heures sur des milliards de lignes), le modèle incrémental ne traite que les nouvelles données.

3. Packages (Réutilisation)

# packages.yml
packages:
  - package: dbt-labs/dbt_utils        # Utilitaires SQL essentiels
    version: [">=1.0.0", "<2.0.0"]
  - package: dbt-labs/codegen          # Génération automatique de staging
    version: [">=0.12.0", "<1.0.0"]
  - package: calogica/dbt_expectations # Tests avancés (regex, ranges, distributions)
    version: [">=0.10.0", "<1.0.0"]
  - package: elementary-data/elementary # Monitoring & observabilité
    version: [">=0.13.0", "<1.0.0"]

4. Seeds (Données de Référence)

Les seeds sont des fichiers CSV versionnés dans git pour les données de référence statiques :

# seeds/country_codes.csv
code,name,region
FR,France,Europe
US,United States,North America
JP,Japan,Asia Pacific

# Puis dans un modèle :
SELECT * FROM {{ ref('country_codes') }}

5. Exposures (Documentation des Usages)

# models/_exposures.yml
exposures:
  - name: weekly_revenue_dashboard
    type: dashboard
    maturity: high
    url: https://metabase.company.com/dashboard/42
    description: "Dashboard hebdomadaire des revenus"
    depends_on:
      - ref('fct_daily_revenue')
      - ref('dim_customers')
    owner:
      name: Équipe Finance
      email: [email protected]

Les Grands Patterns dbt

1. Star Schema (Étoile)

Le pattern le plus classique en data warehousing : une table de faits centrale entourée de tables de dimensions.

Type Préfixe Contient Exemple
Fact (Faits) fct_ Événements, transactions, mesures fct_orders, fct_payments
Dimension dim_ Entités, attributs descriptifs dim_customers, dim_products

2. Wide Table (Table Large)

Alternative au Star Schema pour les cas simples : une seule table dénormalisée avec toutes les informations. Plus simple pour les analystes, moins flexible pour les cas complexes.

3. Activity Schema

Pattern émergent pour les données événementielles : toutes les activités d'un utilisateur dans une seule table avec un schéma flexible (activity_type, feature_1, feature_2, etc.).

4. Surrogate Keys

-- Créer une clé de substitution avec dbt_utils
SELECT
    {{ dbt_utils.generate_surrogate_key(['order_id', 'product_id']) }}
        AS order_line_id,
    order_id,
    product_id,
    quantity,
    unit_price
FROM {{ ref('stg_order_lines') }}

5. Le Pattern « Union Sources »

-- Combiner plusieurs sources du même type
WITH shopify AS (
    SELECT *, 'shopify' AS source_system
    FROM {{ ref('stg_shopify__orders') }}
),
amazon AS (
    SELECT *, 'amazon' AS source_system
    FROM {{ ref('stg_amazon__orders') }}
)
SELECT * FROM shopify
UNION ALL
SELECT * FROM amazon

Best Practices

1
Toujours 3 couches
Staging → Intermediate → Marts. Ne sautez jamais de couche, même si ça semble excessif pour un petit projet.
2
Tests sur TOUT
Chaque modèle doit avoir au minimum unique et not_null sur sa clé primaire. Les marts doivent avoir des tests métier.
3
Documentez chaque modèle
Description du modèle + description de chaque colonne dans les fichiers YAML. C'est la documentation vivante de votre data warehouse.
4
Un staging = un source
Chaque table source a exactement un modèle staging. C'est le seul endroit qui référence source().
5
Utilisez ref() partout
Ne référencez JAMAIS une table directement. Utilisez toujours {{ ref('model_name') }} pour que le DAG soit complet.
6
Source freshness checks
Configurez freshness sur vos sources pour détecter quand les données ne sont plus à jour.
7
SQL lisible > SQL intelligent
Préférez des CTEs nommées clairement à des sous-requêtes imbriquées. Le SQL doit être lisible par un analyste junior.
8
CI/CD sur chaque PR
Utilisez Slim CI pour ne tester que les modèles modifiés dans la PR. C'est essentiel pour la qualité.

Anti-Patterns à Éviter

« Tout dans un seul modèle »

Un seul fichier SQL de 500 lignes qui fait staging + joins + agrégations. Impossible à tester, débugger, ou réutiliser. Découpez en couches !

« source() dans un mart »

Référencer directement une source depuis un mart au lieu de passer par le staging. Vous perdez la couche de nettoyage et le DAG devient incohérent.

« Zéro test »

Pas de tests = pas de confiance dans vos données. Un dashboard qui affiche des chiffres faux est pire que pas de dashboard du tout.

« SELECT * partout »

Ne jamais utiliser SELECT * en production. Listez explicitement les colonnes pour la lisibilité, la performance, et la détection de changements de schéma.

« Logique métier dans la BI »

Si votre calcul de MRR est dans Looker/Metabase au lieu d'être dans dbt, il n'est ni testé, ni versionné, ni réutilisable. Centralisez la logique dans dbt.

« Incrémental par défaut »

Ne rendez un modèle incrémental que quand c'est nécessaire (performance). C'est plus complexe à maintenir et plus facile à casser qu'un simple table.

Les Grandes Questions de Design

1. Star Schema vs Wide Table ?

Critère Star Schema Wide Table
Complexité Plus élevée (plusieurs tables) Faible (une seule table)
Performance BI Excellente (jointures optimisées) Bonne (pas de jointures)
Réutilisabilité Élevée (dimensions partagées) Faible (monolithique)
Quand choisir Plusieurs cas d'usage, équipe data Un seul dashboard, petit projet

2. Quand passer en incrémental ?

3. dbt Core vs dbt Cloud ?

Commencez avec dbt Core si vous avez des compétences techniques et un orchestrateur existant (Airflow, Dagster). Passez à dbt Cloud si votre équipe grandit et que vous voulez un IDE web, un scheduler intégré, et un Slim CI sans configuration.

4. Comment gérer les environnements ?

# profiles.yml
my_project:
  target: dev                    # Environnement par défaut
  outputs:
    dev:
      type: postgres
      schema: dev_{{ env_var('USER') }}   # Schéma isolé par dev
    staging:
      type: postgres
      schema: staging
    prod:
      type: postgres
      schema: analytics          # Le schéma final

Chaque développeur travaille dans son propre schéma (dev_alice, dev_bob). Le staging est partagé. La prod est protégée par CI/CD.

Écosystème & Intégrations

Warehouses Supportés

Snowflake
Le plus populaire. Excellent support, performances top.
BigQuery
Serverless, pay-per-query. Idéal pour les projets Google Cloud.
Redshift
Écosystème AWS. Bon pour les entreprises déjà sur AWS.
DuckDB
Embedded, zéro config. Parfait pour le dev local et les petits projets.

Packages Essentiels

Package Usage Incontournable ?
dbt_utils Macros essentielles (surrogate keys, pivot, unpivot, date spine) ✅ Oui
dbt_expectations Tests avancés (regex, distributions, ranges) ✅ Oui
codegen Génération automatique de staging et YAML ✅ Pour démarrer
elementary Monitoring, anomaly detection, alertes Slack ⭐ Recommandé
dbt_date Dimension dates pré-construite ⭐ Utile
audit_helper Comparer deux versions d'un modèle après refactoring ⭐ Pour les migrations

Pour démarrer avec dbt

  1. pip install dbt-core dbt-postgres (ou dbt-snowflake, dbt-bigquery)
  2. dbt init mon_projet
  3. Configurez votre profiles.yml avec vos credentials warehouse
  4. Créez votre premier modèle staging
  5. dbt run && dbt test
  6. dbt docs generate && dbt docs serve
Vision Globale : Où…Concepts Clés de dbtArchitecture en Couc…Use Cases ActivablesCycle de Vie d'un Pr…Features ClésLes Grands Patterns…Best PracticesAnti-Patterns à Évit…Les Grandes Question…Écosystème & Intégra…