Databricks DE · Topic 1.3
A layered design that improves data quality step by step — Bronze (raw) → Silver (clean) → Gold (business-ready) — all as governed Delta tables.
▸ click each medal to see what it holds
Mechanism: land data as-ingested with minimal transformation, permissive fields, and provenance columns; Auto Loader (cloudFiles) is the typical incremental ingester. Why: a replayable source of truth. Trade-off: don't clean here — it defeats reprocessing/audit.
# Bronze: incremental append ingest with provenance (Auto Loader). (spark.readStream.format("cloudFiles") .option("cloudFiles.format", "json") .option("cloudFiles.schemaLocation", "/Volumes/main/raw/_schema") .load("/Volumes/main/raw/orders") .selectExpr("*", "_metadata.file_path AS src_file", "current_timestamp() AS ingest_ts") .writeStream.option("checkpointLocation", "/Volumes/main/raw/_ckpt") .toTable("main.bronze.orders")) # append-only, raw
Mechanism: cleanse, cast, dedupe, enforce schema, join — typically with MERGE so re-runs are idempotent; keep a non-aggregated record. Trade-off: don't aggregate here — that belongs in gold so many marts reuse one silver.
-- Silver: idempotent upsert of cleaned, deduped records into a conformed table. MERGE INTO main.silver.orders t USING ( SELECT DISTINCT order_id, region, CAST(amount AS DECIMAL(10,2)) AS amount, order_ts FROM main.bronze.orders WHERE order_id IS NOT NULL -- clean + dedupe ) s ON t.order_id = s.order_id WHEN MATCHED THEN UPDATE SET * WHEN NOT MATCHED THEN INSERT *;
Mechanism: aggregate and model for the business (KPIs, star schemas); many gold tables read one silver; gold is what BI & ML consume. Trade-off: purpose-built per use case — expect several gold tables off one silver.
-- Gold: a business-ready aggregate the dashboard/model reads directly. CREATE OR REPLACE TABLE main.gold.daily_revenue_by_region AS SELECT region, date(order_ts) AS day, sum(amount) AS revenue, count(*) AS orders FROM main.silver.orders GROUP BY region, date(order_ts);
Mechanism: each hop is a separation of concerns with its own bar; quality gates (e.g. SDP expectations) drop/quarantine bad rows between layers. Trade-off: medallion is a pattern, not a feature — you implement the gates; more layers = more storage/latency.
# Quality gate between bronze→silver in a Lakeflow SDP pipeline (expectations): from pyspark import pipelines as dp @dp.table() @dp.expect_or_drop("valid_amount", "amount > 0") # drop bad rows at the gate def silver_orders(): return spark.readStream.table("main.bronze.orders")
Mechanism: every layer is a Delta table in the UC namespace; Auto Loader feeds bronze, SDP/jobs build silver→gold, gold serves BI/ML — one copy, one governance model. Trade-off: not every feed needs all three hops — names are a convention.
-- The three layers are just governed Delta tables in the UC 3-level namespace: -- main.bronze.orders (raw, append) ← Auto Loader -- main.silver.orders (clean, deduped) ← MERGE / SDP + expectations -- main.gold.daily_revenue_by_region ← aggregate, serves BI + ML SELECT * FROM main.gold.daily_revenue_by_region; -- what the dashboard queries
▸ click to see what gets rebuilt when silver logic changes