Aller au contenu principal
Structurer un projet dbt : les conventions qui comptent
Retour
Data15 min de lecture

Structurer un projet dbt : les conventions qui comptent

Guillaume HERMANGuillaume HERMAN|Décembre 2025

Pourquoi un projet dbt déraille

Vous connaissez l’histoire. Le projet dbt démarre bien : 10 modèles, une structure simple, tout le monde s’y retrouve. Six mois plus tard, vous avez 200 modèles, des CTE de 500 lignes, des noms comme final_v2_clean_backup, zéro test, et une documentation qui vit dans un Google Doc que personne n’a mis à jour depuis le sprint 3

Ce n’est pas un problème spécifique à dbt. C’est le problème universel du code non structuré. La différence, c’est que dbt est tellement facile à démarrer qu’il encourage la dette technique. Un analyste qui connaît SQL peut créer un modèle dbt en 5 minutes. C’est sa force et son piège

Dans le monde logiciel, personne ne coderait une application sans architecture, sans naming conventions, sans tests. Pourtant, c’est exactement ce que font la majorité des équipes data avec leurs projets dbt. Le résultat est prévisible : un projet qui ralentit, des erreurs silencieuses, et des nouveaux arrivants qui mettent deux semaines à comprendre où trouver quoi

La bonne nouvelle : les conventions qui font la différence ne sont ni compliquées ni coûteuses à mettre en place. Elles demandent juste d’être décidées avant le premier modèle, pas après le deux-centième

La première convention à poser est aussi la plus structurante : comment organiser vos modèles en couches distinctes

Le modèle en 3 layers

Le standard de l’industrie pour structurer un projet dbt est le modèle en 3 layers (couches). Chaque couche a une responsabilité unique et clairement définie

Staging : nettoyer, pas transformer

Le staging est le point d’entrée. Il y a un modèle staging par table source, et sa responsabilité se limite au nettoyage minimal :

  • Renommer les colonnes pour respecter les conventions (snake_case, noms explicites)
  • Caster les types (une date stockée en string → date)
  • Dédupliquer si nécessaire (row_number sur la clé primaire)
  • Filtrer les enregistrements manifestement invalides (nulls sur la clé primaire)

Un modèle staging ne fait jamais de jointures, d’agrégations, ni de logique métier. Si vous vous retrouvez à écrire un JOIN dans un modèle staging, il va dans intermediate

-- models/staging/erp/stg_erp__commandes.sql

with source as (
    select * from {{ source('erp', 'orders') }}
),

renamed as (
    select
        cast(order_id as int) as commande_id,
        cast(customer_id as int) as client_id,
        cast(order_date as date) as date_commande,
        cast(total_amount as numeric(12, 2)) as montant_ttc,
        lower(trim(status)) as statut,
        cast(_loaded_at as timestamp) as charge_le
    from source
)

select * from renamed

Intermediate : la logique métier

Les modèles intermediate portent la logique métier : jointures entre sources, calculs dérivés, enrichissements. C’est ici que vous combinez les commandes avec les clients, calculez les marges, et appliquez les règles de gestion

-- models/intermediate/int_commandes_enrichies.sql

with commandes as (
    select * from {{ ref('stg_erp__commandes') }}
),

clients as (
    select * from {{ ref('stg_crm__clients') }}
),

joined as (
    select
        c.commande_id,
        c.date_commande,
        c.montant_ttc,
        c.statut,
        cl.nom_client,
        cl.segment,
        cl.region
    from commandes c
    left join clients cl on c.client_id = cl.client_id
)

select * from joined

Les modèles intermediate ne sont pas exposés aux consommateurs finaux (BI, API). Ce sont des briques internes. Leur naming commence par int_ pour signaler qu’ils ne doivent pas être requêtés directement par les analystes métier

Marts : les modèles exposés

Les marts sont les modèles finaux, ceux que les utilisateurs de BI et les data scientists consomment. Ils sont optimisés pour la lisibilité et la performance, pas pour la réutilisabilité du code

On distingue deux types de marts :

  • fct_ (faits) : des événements horodatés (commandes, paiements, connexions, incidents). Un fait a une date et un grain.
  • dim_ (dimensions) : des entités référentielles (clients, produits, régions). Une dimension enrichit un fait
-- models/marts/fct_commandes_mensuelles.sql

with commandes as (
    select * from {{ ref('int_commandes_enrichies') }}
)

select
    date_trunc('month', date_commande) as mois,
    segment,
    region,
    count(*) as nombre_commandes,
    sum(montant_ttc) as chiffre_affaires,
    count(distinct commande_id) filter (where statut = 'annulee')
        as commandes_annulees,
    round(avg(montant_ttc), 2) as panier_moyen
from commandes
where statut != 'brouillon'
group by 1, 2, 3

Faut-il un 4e layer ? Certaines équipes ajoutent un layer base (entre source et staging) ou utils (macros et helpers). Notre recommandation : pas avant 100 modèles. Avec 50 modèles, 3 layers suffisent largement. Ajouter des layers prématurément, c’est de la sur-ingénierie qui ralentit l’onboarding sans apporter de valeur

Trois layers bien définis ne servent à rien si les modèles qui les peuplent portent des noms incompréhensibles. Le naming est le complément direct de l’architecture en couches

Naming conventions

Le naming est le premier investissement rentable dans un projet dbt. Un bon naming rend le projet navigable sans documentation : le nom du modèle suffit à comprendre ce qu’il fait et d’où il vient

Préfixes de modèles

Préfixe Layer Exemple
stg_ Staging stg_erp__commandes
int_ Intermediate int_commandes_enrichies
fct_ Marts (fait) fct_commandes_mensuelles
dim_ Marts (dim) dim_clients

Sources : double underscore

Les modèles staging utilisent le pattern stg_{source}__{table}. Le double underscore (__) sépare le système source du nom de la table :

  • stg_erp__commandes → table commandes du système ERP
  • stg_crm__clients → table clients du CRM
  • stg_stripe__paiements → table paiements de Stripe

Ce pattern est précieux quand vous avez des tables de même nom dans des sources différentes (stg_erp__produits vs stg_catalogue__produits)

À noter : le double underscore est une convention dbt Labs largement adoptée, pas une règle technique. Un simple underscore (stg_erp_commandes) reste viable si l’équipe préfère, l’important est la cohérence sur l’ensemble du projet

Colonnes

  • snake_case systématique : date_commande, pas DateCommande ni date-commande
  • Pas de préfixe table dans les colonnes : client_id, pas commande_client_id (sauf en cas d’ambiguïté)
  • Dates : suffixe _at pour les timestamps (cree_le ou created_at), _date pour les dates calendaires (date_commande)
  • Booléens : préfixe est_ ou is_ (est_actif, is_deleted)

Arborescence du projet

models/
├── staging/
│   ├── erp/
│   │   ├── _erp__sources.yml
│   │   ├── _erp__models.yml
│   │   ├── stg_erp__commandes.sql
│   │   ├── stg_erp__produits.sql
│   │   └── stg_erp__clients.sql
│   └── stripe/
│       ├── _stripe__sources.yml
│       ├── _stripe__models.yml
│       └── stg_stripe__paiements.sql
├── intermediate/
│   ├── _int__models.yml
│   ├── int_commandes_enrichies.sql
│   └── int_paiements_reconcilies.sql
└── marts/
    ├── _marts__models.yml
    ├── fct_commandes_mensuelles.sql
    ├── fct_paiements.sql
    ├── dim_clients.sql
    └── dim_produits.sql

Le fichier _*__models.yml par dossier contient les tests et la documentation des modèles de ce dossier. Le préfixe _ le fait apparaître en premier dans l’arborescence. C’est une convention dbt Labs que nous recommandons systématiquement

Un projet bien nommé et bien organisé reste fragile sans vérification automatisée. Les layers et le naming garantissent la lisibilité, les tests garantissent la fiabilité

Tests : le filet de sécurité

dbt rend le testing de données aussi simple que le testing logiciel. Si vous n’écrivez pas de tests dbt, vous avez choisi de découvrir vos erreurs de données en réunion de direction : le scénario cauchemar de tout data analyst

Les 4 tests natifs

dbt fournit quatre tests génériques intégrés, déclarés en YAML :

# models/marts/_marts__models.yml

models:
  - name: fct_commandes_mensuelles
    description: 'Commandes agrégées par mois, segment et région'
    columns:
      - name: mois
        description: 'Premier jour du mois'
        tests:
          - not_null
      - name: nombre_commandes
        tests:
          - not_null
      - name: chiffre_affaires
        tests:
          - not_null
      - name: panier_moyen
        tests:
          - not_null

  - name: dim_clients
    description: 'Référentiel clients enrichi'
    columns:
      - name: client_id
        description: 'Identifiant unique du client'
        tests:
          - unique
          - not_null
      - name: segment
        tests:
          - accepted_values:
              values: ['enterprise', 'mid-market', 'startup', 'particulier']
      - name: region
        tests:
          - not_null

Les quatre tests natifs couvrent 80 % des besoins :

  • not_null : la colonne ne contient pas de valeurs nulles
  • unique : chaque valeur est unique (clés primaires)
  • accepted_values : les valeurs sont dans une liste prédéfinie (statuts, segments)
  • relationships : intégrité référentielle (la FK existe dans la table référencée)

Tests custom

Pour les règles métier spécifiques, dbt permet d’écrire des tests en SQL pur. Un test custom retourne les lignes qui échouent :

-- tests/assert_montant_ttc_positif.sql

select
    commande_id,
    montant_ttc
from {{ ref('fct_commandes_mensuelles') }}
where chiffre_affaires < 0

Si cette requête retourne des lignes, le test échoue. Sinon, il passe. Simple, puissant, et infiniment extensible

Stratégie de couverture

Ne testez pas tout avec la même rigueur. Priorisez par impact :

Layer Couverture cible Tests prioritaires
Marts 100 % (cible) unique, not_null, accepted_values, métier
Intermediate 80 % not_null sur les clés, logique métier
Staging Basique not_null sur la clé primaire, unique

Note : la cible 100 % sur marts s’applique aux marts critiques (KPI exposés, reporting réglementaire). En pratique, on observe 90-95 % selon la maturité des données sources et l’ancienneté du projet.

Pour aller plus loin sur les tests avancés (dbt-expectations, elementary, monitoring en production), consultez notre article dédié Tester ses données comme on teste son code

Les tests détectent les anomalies, mais ils ne disent pas ce qu’un modèle est censé représenter ni pourquoi il existe. Pour ça, il faut documenter, et dbt propose un mécanisme qui élimine l’excuse classique du “pas le temps”

Documentation-as-code

La documentation dbt vit dans les fichiers YAML, à côté du code SQL. C’est le principe du docs-as-code : la documentation est versionnée, revue en code review, et synchronisée avec le code dès lors que la code review impose la mise à jour des descriptions à chaque PR. Sans cette discipline, la doc dérive comme partout ailleurs

# models/marts/_marts__models.yml (extrait)

models:
  - name: dim_clients
    description: >
      Référentiel clients enrichi. Combine les données CRM (identité, segment)
      avec l'historique de commandes (première commande, dernière commande,
      nombre total de commandes). Mis à jour quotidiennement.
    columns:
      - name: client_id
        description: 'Identifiant unique du client, issu du CRM'
      - name: segment
        description: >
          Segment commercial du client. Valeurs possibles :
          enterprise, mid-market, startup, particulier.
          Déterminé par le chiffre d'affaires annuel.
      - name: date_premiere_commande
        description: 'Date de la première commande validée du client'
      - name: nombre_commandes_total
        description: 'Nombre total de commandes validées (hors brouillons et annulées)'

La commande dbt docs generate produit un site statique navigable avec :

  • La liste de tous les modèles avec leurs descriptions
  • Le détail de chaque colonne
  • Un graphe de lineage interactif montrant les dépendances entre modèles
  • Les résultats des derniers tests

Ce site est déployable en interne (un simple serveur web statique) et remplace avantageusement le Confluence que personne ne met à jour

La règle d’or : la documentation est revue en code review, comme le code. Une pull request qui ajoute un modèle sans description est refusée. Ce n’est pas du perfectionnisme, c’est la seule façon de maintenir une documentation à jour dans la durée

Layers, naming, tests, documentation : ces quatre piliers se renforcent mutuellement. Voici ce que donne leur application conjointe sur un projet réel

Retour terrain : restructurer un projet dbt existant

Une ETI du REX composite Nobori (cas représentatif anonymisé issu de plusieurs missions 2023-2026) a démarré son projet dbt sans conventions. Après 3 mois et 80 modèles à plat, le constat était sans appel :

Avant la restructuration :

  • 80 modèles dans un dossier unique (pas de layers)
  • Naming incohérent : commandes_final, clients_v2, monthly_revenue_clean
  • 0 tests
  • Documentation sur Confluence, obsolète depuis 6 semaines
  • Onboarding d’un nouvel analyste : 2 semaines pour comprendre le projet

Nous avons mené une restructuration sur 2 semaines, en parallèle de la production (pas d’interruption de service ; calendrier tendu mais tenable si l’équipe a déjà une bonne maîtrise SQL et Git) :

  1. Semaine 1 : réorganisation en 3 layers (staging/intermediate/marts), renaming systématique avec les préfixes standards, ajout des fichiers _*__models.yml
  2. Semaine 2 : ajout de tests (unique + not_null sur toutes les clés, accepted_values sur les statuts critiques), documentation des 20 marts principaux

Après la restructuration :

  • 3 layers propres, 82 modèles restructurés
  • Couverture de tests : 95 % sur les marts, 70 % sur intermediate
  • Documentation générée par dbt docs generate, déployée en interne
  • Onboarding d’un nouvel analyste : 2 jours

Le gain le plus spectaculaire n’est pas technique : c’est la confiance. Les équipes métier qui consommaient les données ont rapidement remarqué que les erreurs étaient détectées avant d’arriver dans les dashboards. La satisfaction interne est passée de 3,2/10 à 8,1/10 en deux mois

Ce retour d’expérience illustre le chemin idéal. En pratique, la structuration comporte des pièges récurrents que nous retrouvons d’un projet à l’autre

Les 4 pièges de la structuration

1. Sur-ingénierer les layers. Trois layers suffisent pour 90 % des projets. Ajouter des layers base, utils, reporting, analytics avant d’avoir 100 modèles, c’est créer de la complexité sans bénéfice. Commencez simple, ajoutez un layer quand la douleur est réelle, pas quand elle est théorique

2. Copier-coller une convention GitHub sans l’adapter. Le dbt style guide de GitLab est excellent, mais il a été conçu pour une organisation de 2 000 personnes avec 500 modèles dbt. Votre ETI de 200 personnes avec 50 modèles n’a pas les mêmes besoins. Prenez les principes (layers, préfixes, tests), adaptez le reste à votre contexte

3. Ignorer la dette existante. “On structurera le projet plus tard, quand on aura le temps.” Ce moment n’arrive jamais. La dette s’accumule de manière exponentielle : chaque modèle ajouté sans convention rend la restructuration plus coûteuse. Si vous avez déjà 50+ modèles sans structure, planifiez un sprint de restructuration maintenant, pas au prochain trimestre

4. Ne pas impliquer les analystes dans les conventions. Des conventions imposées par l’architecte data sans consultation des analystes seront contournées. Impliquez l’équipe dans la définition des règles : quels préfixes, quelles colonnes sont obligatoires, quel niveau de documentation. Les conventions co-construites sont les seules qui tiennent

Conclusion

Structurer un projet dbt n’est ni difficile ni coûteux. C’est une décision, prise au bon moment (dès le départ) ou au mauvais moment (après 200 modèles), mais c’est la même décision

Les conventions présentées ici (3 layers, naming avec préfixes, tests systématiques, documentation-as-code) ne sont pas des opinions. Ce sont les standards de l’industrie, validés par des milliers de projets dbt en production. Vous pouvez les adapter à votre contexte, mais les ignorer, c’est s’engager sur un chemin que chaque équipe data regrette

Nobori accompagne les ETI dans la structuration et l’industrialisation de leurs projets dbt. Découvrir notre offre Data

Sources et méthodologie

  • dbt Labs, “Best practices guide” : conventions de structure, naming, testing recommandées par dbt Labs
  • GitLab, “dbt style guide” : référence communautaire pour les conventions dbt en entreprise
  • Retours d’expérience Nobori : données issues de nos missions de structuration dbt auprès d’ETI (métriques avant/après anonymisées)

Les métriques du retour terrain sont issues de mesures réelles. La recommandation des 3 layers comme standard est basée sur la documentation officielle dbt Labs et nos observations sur une dizaine de missions accompagnées entre 2023 et 2026.

Pour aller plus loin

Ce sujet vous concerne ?

Évaluez votre maturité en Data & Analytics en 30 min avec un expert senior. Sans engagement

Guillaume HERMAN

Guillaume HERMAN

Lead Data

AWS SA ProfessionalGCP Pro Cloud ArchitectTerraform Associate

Ce sujet vous intéresse ?

Évaluez votre maturité avec un diagnostic de 30 min.

Newsletter

Restez informé

Analyses Cloud, Data & IA : 1 email par mois, pas plus

Réserver un appel découverte