ServicesÀ proposNotesContact Me contacter →
EN FR
Note

Patterns de modèles packageables dbt

Trois patterns qui rendent les modèles dbt installables par n'importe qui — sources configurables avec var(), flags d'activation/désactivation et noms de modèles avec namespace.

Planté
dbtdata modelingdata engineering

Trois techniques rendent les modèles dbt installables par n’importe qui : sources configurables avec var(), flags d’activation/désactivation et noms de modèles avec namespace. L’approche de l’équipe Fivetran pour ces patterns est devenue un standard de facto. Omettre l’un d’eux cassera les installations dans des environnements avec des structures de schémas ou de nommage différentes.

Sources configurables avec var()

Ne codez jamais en dur l’emplacement des données source. Ce qui est raw_stripe.payments dans votre warehouse est analytics.stripe_data.payment_events dans celui de quelqu’un d’autre. La fonction var() avec des valeurs par défaut sensées gère cela proprement.

Définition de source

models/base/_sources.yml
sources:
- name: my_package
schema: "{{ var('my_package_schema', 'my_data') }}"
database: "{{ var('my_package_database', target.database) }}"
tables:
- name: events
identifier: "{{ var('my_package_events_identifier', 'events') }}"

Trois niveaux de configurabilité ici :

  1. Schéma — où se trouvent les tables source. La plupart des utilisateurs devront le modifier.
  2. Base de données — par défaut sur la base de données target de l’utilisateur. Nécessaire uniquement pour les configurations cross-database.
  3. Identifiant — le nom réel de la table. Gère les cas où une table a un nom différent dans le warehouse de quelqu’un (raw_events au lieu d’events).

Modèle base

-- models/base/base__my_package__events.sql
WITH source AS (
SELECT
event_id,
event_name,
event_timestamp,
user_id
FROM {{ source('my_package', 'events') }}
)
SELECT
event_id,
event_name,
event_timestamp,
user_id
FROM source

Le modèle base référence source(), qui résout le schéma, la base de données et l’identifiant depuis la définition YAML. Les utilisateurs pointent le package vers leur propre schéma en définissant my_package_schema dans leur dbt_project.yml :

# dbt_project.yml de l'utilisateur
vars:
my_package_schema: 'raw_stripe'
my_package_events_identifier: 'payment_events'

C’est le même pattern de couche base utilisé dans les projets ordinaires, avec var() encapsulant les parties qui diffèrent selon les environnements.

Convention de nommage des variables

Suivez la convention établie par Fivetran et dbt Labs :

ParamètreNom de variableExemple
Schéma{package}_schemamy_package_schema
Base de données{package}_databasemy_package_database
Identifiant de table{package}_{table}_identifiermy_package_events_identifier
Feature flag{package}__{modèle}_enabledmy_package__daily_summary_enabled

Le double underscore avant le nom de modèle dans les feature flags sépare le namespace du package du nom du paramètre, évitant l’ambiguïté avec les noms de packages multi-mots.

Activer/désactiver des modèles

Tous les utilisateurs n’ont pas besoin de chaque modèle de votre package. Les flags d’activation/désactivation permettent aux utilisateurs de désactiver les parties dont ils n’ont pas besoin, réduisant le temps de build et l’encombrement du warehouse.

-- models/marts/my_package__daily_summary.sql
{{ config(enabled=var('my_package__daily_summary_enabled', true)) }}
SELECT
DATE(event_timestamp) AS event_date,
COUNT(DISTINCT user_id) AS unique_users,
COUNT(*) AS total_events
FROM {{ ref('base__my_package__events') }}
GROUP BY 1

La config enabled accepte directement un appel var(). Par défaut à true pour que les modèles soient inclus sauf désactivation explicite. Les utilisateurs se désabonnent dans leur dbt_project.yml :

vars:
my_package__daily_summary_enabled: false

Quand un modèle est désactivé, dbt le ignore entièrement — pas de compilation, pas d’exécution, pas d’objet warehouse. Les modèles en aval qui référencent un modèle désactivé échoueront, donc documentez quels modèles peuvent être désactivés indépendamment. Un pattern courant est de regrouper les modèles en « modules » avec un seul flag :

vars:
my_package__daily_models_enabled: true
my_package__weekly_models_enabled: true
my_package__monthly_models_enabled: false # Désactiver toutes les agrégations mensuelles

Noms de modèles avec namespace

Chaque modèle de votre package devrait commencer par le nom du package. Si votre package s’appelle revenue_tools, nommez vos modèles revenue_tools__monthly_mrr et revenue_tools__churn_events, pas monthly_mrr et churn_events.

Cela évite les collisions de nommage lorsque les utilisateurs ont plusieurs packages installés. Sans préfixes, deux packages définissant tous deux un modèle customers entreront en conflit, et les messages d’erreur ne sont pas toujours clairs sur la cause.

La convention s’applique à tous les types de modèles :

CoucheExemple
Basebase__my_package__events
Intermédiairemy_package__enriched_events
Martmy_package__daily_summary

Les modèles base suivent la convention de nommage base standard (base__source__entity) avec le nom du package comme source. Les modèles des couches supérieures utilisent le nom du package directement comme préfixe.

C’est verbeux, mais les collisions entre packages sont l’une des expériences de débogage les plus frustrantes en dbt. Un utilisateur ayant fivetran_stripe, fivetran_shopify et votre package revenue_tools installés a besoin de noms de modèles sans ambiguïté.

En synthèse

Voici une configuration complète de source et de modèle base pour un package appelé ad_analytics :

models/base/_sources.yml
sources:
- name: ad_analytics
schema: "{{ var('ad_analytics_schema', 'raw_ads') }}"
database: "{{ var('ad_analytics_database', target.database) }}"
tables:
- name: campaigns
identifier: "{{ var('ad_analytics_campaigns_identifier', 'campaigns') }}"
- name: ad_spend
identifier: "{{ var('ad_analytics_ad_spend_identifier', 'ad_spend') }}"
-- models/base/base__ad_analytics__campaigns.sql
WITH source AS (
SELECT *
FROM {{ source('ad_analytics', 'campaigns') }}
),
renamed AS (
SELECT
campaign_id,
campaign_name,
platform,
start_date,
end_date,
budget_amount,
currency_code
FROM source
)
SELECT * FROM renamed
-- models/marts/ad_analytics__daily_spend.sql
{{ config(enabled=var('ad_analytics__daily_spend_enabled', true)) }}
SELECT
DATE(spend_date) AS spend_date,
c.campaign_name,
c.platform,
SUM(s.amount) AS total_spend,
SUM(s.impressions) AS total_impressions,
SUM(s.clicks) AS total_clicks
FROM {{ ref('base__ad_analytics__ad_spend') }} s
LEFT JOIN {{ ref('base__ad_analytics__campaigns') }} c
ON s.campaign_id = c.campaign_id
GROUP BY 1, 2, 3
# dbt_project.yml (valeurs par défaut du package)
vars:
ad_analytics_schema: 'raw_ads'
ad_analytics_database: null
ad_analytics__daily_spend_enabled: true

Les utilisateurs personnalisent avec une configuration minimale :

# dbt_project.yml de l'utilisateur
vars:
ad_analytics_schema: 'my_company_ads'

Tout le reste utilise les valeurs par défaut. L’utilisateur obtient un package fonctionnel avec une seule ligne de configuration.