ServicesÀ proposNotesContact Me contacter →
EN FR
Note

Patterns de la couche Mart dbt

Ce qui appartient aux modèles mart dbt — agrégations de reporting, exports d'activation, tables de features ML — et le principe selon lequel chaque mart sert un consommateur spécifique.

Planté
dbtdata modelingdata engineering

Les modèles mart constituent la couche orientée consommateur d’un projet dbt. Contrairement aux modèles base (le fondement du projet) et aux modèles intermédiaires (l’enrichissement des entités pour la réutilisation interne), les marts existent pour des cas d’usage spécifiques. Chaque mart a un consommateur défini et une granularité définie — une question précise à un niveau d’agrégation précis.

C’est la première et seule couche dans l’Architecture trois couches dbtGROUP BY apparaît en sortie finale. Les marts réduisent le nombre de lignes parce que c’est ce dont les consommateurs ont réellement besoin : non pas chaque commande, mais les performances quotidiennes par canal. Non pas chaque événement client, mais une ligne par client avec tous ses attributs.

Les Trois Principaux Patterns de Mart

Marts de Reporting

Les marts de reporting agrègent à la granularité dont vos dashboards ont besoin. Performances quotidiennes par canal. Rétention de cohorte hebdomadaire. Revenu mensuel par segment. La granularité est déterminée par ce que l’équipe BI interroge le plus fréquemment — construire un mart à la mauvaise granularité signifie que l’outil BI réalise l’agrégation à sa place, ce qui est plus lent et moins cohérent.

Pour les projets BigQuery, réfléchissez à la façon dont le partitionnement et le clustering affectent ces patterns d’agrégation. Un mart de reporting quotidien partitionné par date et clusterisé par canal vous offre des requêtes efficaces d’emblée.

{{ config(
materialized='table',
partition_by={
"field": "date_day",
"data_type": "date",
"granularity": "day"
},
cluster_by=['channel__name'],
tags=['mart', 'reporting', 'marketing']
) }}
WITH int__orders AS (
SELECT
order__id,
order__created_at,
order__amount_usd,
customer__id,
channel__name,
order__margin_usd,
order__is_first_order,
order__is_completed
FROM {{ ref('int__order__enriched') }}
WHERE order__is_completed = TRUE
),
aggregated AS (
SELECT
DATE(order__created_at) AS date_day,
channel__name,
COUNT(DISTINCT order__id) AS orders,
COUNT(DISTINCT customer__id) AS customers,
SUM(CASE WHEN order__is_first_order THEN 1 ELSE 0 END) AS new_customers,
SUM(order__amount_usd) AS revenue_usd,
SUM(order__margin_usd) AS margin_usd,
SUM(CASE WHEN order__is_first_order THEN order__amount_usd ELSE 0 END) AS new_customer_revenue_usd
FROM int__orders
GROUP BY ALL
),
enriched AS (
SELECT
*,
SAFE_DIVIDE(margin_usd, revenue_usd) AS margin_rate,
SAFE_DIVIDE(revenue_usd, orders) AS avg_order_value_usd,
SAFE_DIVIDE(new_customers, customers) AS new_customer_rate
FROM aggregated
)
SELECT
date_day,
channel__name,
orders,
customers,
new_customers,
revenue_usd,
margin_usd,
margin_rate,
avg_order_value_usd,
new_customer_revenue_usd,
new_customer_rate
FROM enriched

Notez la structure des CTE : import depuis l’intermédiaire, agrégation à la granularité cible, puis enrichissement avec des métriques dérivées (taux, moyennes). La fonction SAFE_DIVIDE prévient les erreurs de division par zéro qui produiraient sinon des NULL ou des échecs dans votre dashboard.

Le raccourci GROUP BY ALL (disponible dans BigQuery et certains autres entrepôts) est pratique mais controversé — certaines équipes préfèrent des listes de colonnes explicites pour plus de clarté. Dans tous les cas, le point essentiel est que c’est la première fois dans la lignée où les lignes sont réduites.

Marts d’Activation

Les marts d’activation préparent les données pour des destinations de reverse ETL : outils CRM comme Braze ou Salesforce, plateformes d’email, audiences publicitaires, plateformes de données clients. La granularité est généralement une ligne par entité, avec tous les attributs que le système de destination attend.

{{ config(
materialized='table',
tags=['mart', 'activation', 'braze']
) }}
WITH int__customers AS (
SELECT
customer__id,
customer__email,
customer__segment,
customer__orders,
customer__total_spent_usd,
customer__last_ordered_at
FROM {{ ref('int__customer__enriched') }}
),
enriched AS (
SELECT
customer__id,
customer__email,
customer__segment,
customer__orders,
customer__total_spent_usd,
customer__last_ordered_at,
CASE
WHEN customer__orders = 0 THEN 'prospect'
WHEN customer__orders = 1 THEN 'new'
WHEN customer__last_ordered_at >= DATE_SUB(CURRENT_DATE(), INTERVAL 90 DAY) THEN 'active'
WHEN customer__last_ordered_at >= DATE_SUB(CURRENT_DATE(), INTERVAL 365 DAY) THEN 'lapsing'
ELSE 'churned'
END AS customer__lifecycle_stage,
customer__orders >= 5 AS customer__is_vip,
customer__total_spent_usd >= 1000 AS customer__is_high_ltv
FROM int__customers
)
SELECT
customer__email AS email,
customer__id AS external_id,
customer__segment,
customer__lifecycle_stage AS lifecycle_stage,
customer__orders AS orders,
customer__total_spent_usd AS total_spent_usd,
customer__is_vip AS is_vip,
customer__is_high_ltv AS is_high_ltv
FROM enriched

Deux éléments distinguent les marts d’activation des marts de reporting. Premièrement, les noms de colonnes dans le SELECT final correspondent à ce que le système de destination attend (email, external_id, lifecycle_stage) plutôt qu’à votre convention de nommage interne. Deuxièmement, la table n’expose que ce dont la destination a besoin — rien de plus. Braze n’a pas besoin de votre format d’identifiant client interne ni des horodatages de commande bruts. Garder la sortie compacte réduit les coûts de transfert de données et évite que des données sensibles ne fuient vers des systèmes externes.

Marts ML de Features

Les marts ML créent des tables de features pour les pipelines de machine learning. Ils tendent à être larges (nombreuses colonnes) avec des features temporelles, une ligne par cible de prédiction. La granularité correspond à ce que le modèle ML attend : une ligne par client pour la prédiction de churn, une ligne par produit pour la prévision de la demande, une ligne par session pour la prédiction de conversion.

Les marts ML incluent souvent des features décalées (métriques de la période précédente), des agrégations glissantes (moyennes sur 30 jours), et des features de ratio que les data scientists ont identifiées comme prédictives. La couche intermédiaire fournit les briques de base ; le mart les assemble dans la matrice de features que le pipeline d’entraînement du modèle consomme.

Chaque Mart Sert un Consommateur Spécifique

Avant de créer un mart, trois questions doivent avoir des réponses claires :

  • Qui le consomme ? Un dashboard spécifique, un pipeline de reverse ETL, un job d’entraînement ML.
  • Quelle granularité lui faut-il ? Quotidien par canal, une ligne par client, une ligne par session.
  • Quelles colonnes lui faut-il ? Uniquement ce qu’il interrogera ou ingérera réellement.

Si ces questions n’ont pas de réponses claires, le cas d’usage est peut-être mieux servi par un modèle intermédiaire bien construit.

Quand Vous N’avez Pas Besoin d’un Mart

Un mart qui n’est qu’un SELECT * FROM int__order__enriched avec un autre nom ajoute un nœud à votre DAG sans apporter de valeur. Si vous ne changez pas la granularité, si vous ne formatez pas pour un consommateur spécifique, et si vous n’agrégez pas — vous n’avez probablement pas besoin d’un mart. Les consommateurs peuvent interroger directement le modèle intermédiaire, ou vous pouvez l’exposer comme modèle public avec un contrat.

Le test : si supprimer le mart et pointer les consommateurs vers le modèle intermédiaire ne changeait pas leurs requêtes, le mart est un surcoût inutile.

Organisation des Marts

Les marts sont organisés par domaine métier, non par système source ou par entité. La structure du projet les place dans des dossiers comme marts/finance/, marts/marketing/, marts/activation/. Cela reflète la façon dont les consommateurs pensent : l’équipe marketing cherche dans le dossier marketing, pas dans un dossier nommé d’après le système source dont proviennent leurs données.

Les noms suivent le pattern mrt__[domaine]__[entité_ou_objectif] :

  • mrt__marketing__daily_channel_performance
  • mrt__finance__monthly_revenue
  • mrt__activation__braze_customers
  • mrt__ml__customer_churn_features

Le préfixe de domaine indique clairement qui possède et consomme chaque table.

Considérations de Matérialisation

La plupart des marts doivent être matérialisés en table pour des performances de requête rapides. Les exceptions :

  • Les marts de reporting à fort volume avec des données historiques bénéficient de la matérialisation incrémentale. Un mart de performances quotidiennes qui croît d’une ligne par jour et par canal n’a pas besoin de recalculer tout l’historique à chaque exécution.
  • Les marts de reporting BigQuery doivent utiliser partition_by sur la colonne de date et cluster_by sur les colonnes de filtre les plus courantes. Cela donne à l’outil BI des patterns d’accès efficaces sans scans complets de table.
  • Les marts d’activation sont généralement assez petits (une ligne par client) pour que la matérialisation en table avec rafraîchissement complet soit adaptée. La complexité de la logique incrémentale n’en vaut pas la peine quand la table tient en mémoire.

Tester les Marts

Les marts méritent l’investissement de test le plus important dans la taxonomie de tests :

  • Tests de clé primaire sur les colonnes de granularité (date + canal pour un mart quotidien, customer_id pour un mart client)
  • Tests not-null sur toutes les colonnes de métriques — un NULL dans revenue_usd cassera silencieusement les totaux du dashboard
  • Valeurs acceptées sur les colonnes catégorielles sur lesquelles les consommateurs filtrent
  • Contrats de modèle pour les marts publics, imposant la stabilité du schéma pour que les outils BI ne se cassent pas lors des refactorisations
  • Tests de plage dbt-expectations pour les métriques métier — le revenu ne doit jamais être négatif, les taux doivent être compris entre 0 et 1

Les enjeux sont plus élevés ici parce que les marts sont ce que les consommateurs voient. Un bug dans un modèle intermédiaire est invisible jusqu’à ce qu’il remonte dans un mart. Un bug dans un mart est immédiatement visible dans un dashboard ou une synchronisation CRM.