Quand nous avons repris l’infrastructure data d’une banque régionale en Afrique de l’Ouest, nous avons hérité d’une collection de jobs batch nocturnes, de scripts SQL éparpillés et d’un entrepôt de données que personne ne comprenait vraiment. Les requêtes prenaient plus de 40 minutes. Les rapports étaient inexacts le lendemain matin. L’équipe analytique avait arrêté de faire confiance aux données.
Nous l’avons reconstruit autour de l’Architecture Médaillon en huit mois. Voici à quoi ressemblait concrètement l’implémentation : les décisions, les erreurs, et les parties que nous referions différemment.
Qu’est-ce que l’Architecture Médaillon (et ce qu’elle n’est pas)
L’Architecture Médaillon (aussi appelée architecture multi-hop) organise le stockage d’un data lake en trois zones :
- Bronze : données brutes, ingérées telles quelles depuis les sources. Immuable. Votre filet de sécurité.
- Silver : données nettoyées, validées et enrichies. Schéma appliqué, doublons supprimés, types corrigés.
- Gold : agrégats métier, prêts pour la consommation analytique et les tableaux de bord.
Ce n’est pas une obligation d’utiliser trois couches exactement. Ce n’est pas non plus une technologie spécifique. C’est un pattern d’organisation des données qui sépare la préoccupation d’ingestion de celle de transformation.
La Couche Bronze : Ingestion Sans Transformation
La règle d’or : ne transformez jamais les données en Bronze. Stockez-les exactement comme elles arrivent.
Notre première erreur a été de “nettoyer un peu” en Bronze. Nous avons normalisé les noms de colonnes et supprimé certains champs que nous pensions inutiles. Trois mois plus tard, nous avions besoin exactement de ces champs pour un audit réglementaire. Sans Bronze immuable, nous aurions été bloqués.
# Opérateur Airflow pour l'ingestion Bronze@taskdef ingest_to_bronze(source_id: str, date: str) -> str: """Ingère les données brutes vers Bronze sans aucune transformation.""" raw_data = fetch_from_source(source_id, date)
path = f"s3://datalake/bronze/{source_id}/date={date}/data.parquet" raw_data.to_parquet(path, index=False)
# Enregistrer les métadonnées d'ingestion log_ingestion_metadata(source_id, date, len(raw_data), path) return pathPartitionnement : partitionnez toujours par date en Bronze. Vos transformations Silver n’auront besoin que d’un jour à la fois. Le full scan de toutes les données brutes est la cause numéro un des pipelines lents.
La Couche Silver : Où Vit la Qualité des Données
Silver est la couche la plus complexe. C’est ici que vous définissez ce que signifient vos données.
Règles que nous appliquons en Silver :
- Validation de schéma - chaque colonne a un type défini, les nulls sont autorisés ou non
- Déduplication - basée sur les clés métier, pas sur les identifiants techniques
- Normalisation - formats de date unifiés, codes pays standardisés ISO
- Référencement - les IDs étrangers sont validés contre les tables de référence
# Exemple de validation Silver avec Great Expectationsimport great_expectations as ge
def validate_transactions_silver(df): gdf = ge.from_pandas(df)
results = gdf.expect_column_values_to_not_be_null("transaction_id") results &= gdf.expect_column_values_to_be_between( "amount", min_value=0, max_value=10_000_000 ) results &= gdf.expect_column_values_to_match_regex( "account_id", r"^[A-Z]{2}\d{10}$" )
if not results["success"]: raise DataQualityError(f"Validation Silver échouée: {results}") return dfLa Couche Gold : Modèles Métier avec dbt
Gold est là où les analystes et les applications vivent. Nous l’avons construit entièrement avec dbt.
La structure de notre projet dbt :
models/ gold/ finance/ fct_transactions_daily.sql fct_credit_applications.sql dim_customers.sql risk/ fct_fraud_indicators.sql mart_credit_scoring.sql reporting/ rpt_executive_dashboard.sqlUn exemple de modèle Gold pour les indicateurs de risque client :
-- models/gold/risk/mart_credit_scoring.sql{{ config(materialized='table', tags=['daily', 'risk']) }}
with customer_transactions as ( select customer_id, count(*) as transaction_count_90d, sum(amount) as total_volume_90d, avg(amount) as avg_transaction_amount, stddev(amount) as transaction_volatility from {{ ref('fct_transactions_daily') }} where transaction_date >= dateadd('day', -90, current_date) group by 1),
payment_history as ( select customer_id, sum(case when status = 'late' then 1 else 0 end) as late_payments_count, max(days_overdue) as max_days_overdue from {{ ref('fct_credit_applications') }} group by 1)
select ct.customer_id, ct.transaction_count_90d, ct.total_volume_90d, ct.avg_transaction_amount, ct.transaction_volatility, coalesce(ph.late_payments_count, 0) as late_payments_count, coalesce(ph.max_days_overdue, 0) as max_days_overduefrom customer_transactions ctleft join payment_history ph using (customer_id)L’Orchestration avec Airflow
Notre DAG principal orchestre les trois couches de façon séquentielle avec gestion des dépendances :
with DAG('medallion_daily', schedule='0 2 * * *', catchup=False) as dag:
# Couche Bronze : ingestion parallèle par source bronze_tasks = [ ingest_to_bronze.override(task_id=f"bronze_{src}")(src, "{{ ds }}") for src in DATA_SOURCES ]
# Couche Silver : après Bronze complet silver_validate = validate_and_transform_silver("{{ ds }}")
# Couche Gold : dbt run gold_build = BashOperator( task_id="gold_dbt_run", bash_command="dbt run --select gold --target prod" )
# Alertes qualité des données quality_check = DataQualityOperator( task_id="quality_gate", tables=["gold.mart_credit_scoring"], thresholds={"completeness": 0.99, "freshness_hours": 4} )
bronze_tasks >> silver_validate >> gold_build >> quality_checkCe que Nous Referions Différemment
1. Investir plus tôt dans le catalogage des données. À mesure que Gold grossissait, personne ne savait plus quelle table Gold était la “source de vérité” pour les métriques clients. Nous avons passé un mois à documenter rétroactivement. Configurez DataHub ou OpenMetadata dès le premier sprint.
2. Définir des SLA par couche. Bronze doit être disponible dans les 30 minutes suivant la source. Silver dans les 2 heures. Gold avant 7h du matin. Sans SLA formels, les retards s’accumulent en silence.
3. Tester les transformations comme du code. Nous avons traité les modèles dbt comme des scripts jetables pendant trop longtemps. Les tests de régression sur les transformations Silver auraient évité trois incidents de production.
4. Prévoir la rétrofacturation dès le départ. Quand 12 équipes métier consomment les mêmes tables Gold, des questions de coût et de priorité émergent. Avoir les tags et les métriques d’utilisation dès le début facilite les conversations difficiles.
L’Architecture Médaillon n’est pas magique. C’est de la discipline opérationnelle appliquée à l’organisation des données. Le vrai bénéfice n’est pas technique, c’est organisationnel : tout le monde sait où chercher les données, dans quel état elles sont, et qui en est responsable.