DE Track · Module 06

Lakehouse Data Modeling

Medallion done right, dimensions on Delta, and the CDC/SCD patterns enterprise interviews probe.

Medallion architecture with real semantics

Everyone can recite Bronze/Silver/Gold. What separates a strong answer is precise semantics per layer — what guarantees each layer makes and to whom. Treat each layer as a contract:

LayerContractKey propertiesConsumers
Bronze "We captured exactly what the source sent, and we can replay it." Raw, append-only, schema-on-read (or permissive schema), ingestion metadata (load timestamp, source file/batch id, CDC op code). Never updated in place; reprocessable forever. Engineers only. Never BI.
Silver "This is the conformed, trustworthy version of the entity." Deduplicated, typed, quality-enforced, business keys resolved, SCD history applied, conformed across source systems. One row means one real-world fact or entity version. Data scientists, downstream pipelines, some power analysts.
Gold "This answers a business question with agreed definitions." Dimensional models, aggregates, KPI tables. Owned definitions (what counts as "revenue"), serving-optimized layout (liquid clustering on join/filter keys). BI tools, semantic layers, executives, GenAI agents.

The point of Bronze being append-only and replayable is operational: when a Silver transform has a bug, you fix the code and rebuild Silver from Bronze. You never ask SAP to resend three years of journal entries. That replay argument is the one to lead with in interviews.

Anti-patterns to call out (interviewers love hearing you name them):

  • Medallion as religion. Three layers is a default, not a law. A reference table from a clean API may not need a heavy Silver step; a complex finance domain may need a sub-layer between Silver and Gold for hierarchy flattening. Say "the layer count follows the semantics, not the other way around."
  • Gold sprawl. Every team spinning its own "gold" aggregate with slightly different P&L definitions. Cure: conformed dimensions, a small set of certified Gold marts, Unity Catalog ownership and certification tags, and pushing variant logic into the semantic layer instead of new tables.
  • Skipping Bronze. Landing transformed data directly to Silver "to save storage" destroys replayability and audit. Storage is the cheapest thing in the stack; re-extraction from an ERP is the most expensive.
  • Bronze-as-dumping-ground with no metadata. Without load timestamps and source batch ids, you cannot do incremental Silver loads or trace a bad number back to a file.

Dimensional modeling still matters on the lakehouse

Delta did not repeal Kimball. Star schemas survive because BI tools, the columnar engines underneath them, and humans all reason best over facts joined to conformed dimensions. What changes on Databricks:

At ADM you lead Finance Record-to-Report analytics end to end: Bronze/Silver/Gold medallion pipelines on Azure Databricks + ADF integrating JDE, SAP, HFM (Hyperion), IBM DB2, and IBM APGO Web Wire into Delta Lake under Unity Catalog governance. The Gold layer is exactly this conformed-dimension story: P&L, Budget vs Actual, Expense Forecasting, EHS, and Capital Allocation KPI frameworks all consumed at Executive Committee / CFO level — which only works because the entity, account, and period dimensions are conformed once and reused across every KPI mart.

SCD Type 1 and Type 2 on Delta

Type 1 (overwrite, no history) is a plain MERGE INTO: update on match, insert on no match. Use it for correction-style attributes where history has no business meaning.

Type 2 (full history with validity windows) is the pattern interviews drill. The classic single-statement trick: union the staged changes with a NULL-keyed copy, so one source row expires the current version and its NULL-keyed twin inserts the new version.

-- Staged changes already deduplicated to one row per business key
MERGE INTO silver.dim_customer AS tgt
USING (
  -- Rows that match current records: used to close them out
  SELECT s.customer_id AS merge_key, s.*
  FROM stg_customer_changes s

  UNION ALL

  -- NULL merge_key never matches, so these fall through to INSERT
  SELECT NULL AS merge_key, s.*
  FROM stg_customer_changes s
  JOIN silver.dim_customer t
    ON  s.customer_id = t.customer_id
    AND t.is_current = true
  WHERE s.attr_hash <> t.attr_hash          -- only real changes spawn a new version
) AS src
ON  tgt.customer_id = src.merge_key
AND tgt.is_current  = true

WHEN MATCHED AND tgt.attr_hash <> src.attr_hash THEN
  UPDATE SET tgt.is_current = false,
             tgt.valid_to   = src.change_ts

WHEN NOT MATCHED THEN
  INSERT (customer_id, customer_name, segment, attr_hash,
          valid_from, valid_to, is_current)
  VALUES (src.customer_id, src.customer_name, src.segment, src.attr_hash,
          src.change_ts, TIMESTAMP'9999-12-31', true);
-- customer_sk is GENERATED ALWAYS AS IDENTITY: omit it from the INSERT list

Design notes that show seniority: compare an attr_hash (hash of tracked columns) instead of column-by-column comparison; pick a deterministic valid_to sentinel; keep is_current as a flag so BI filters are cheap; and cluster the dimension on the business key.

MERGE pitfall: if the staged source has two rows for the same business key, MERGE fails with "multiple source rows matched a single target row" — or worse, silently produces overlapping validity windows if you pre-collapsed wrongly. Always dedupe the source first with ROW_NUMBER() OVER (PARTITION BY key ORDER BY change_ts DESC) (Type 1/2 latest-wins) or process intermediate versions in sequence order if the business needs every state.

The declarative alternative: AUTO CDC in Lakeflow Spark Declarative Pipelines

Lakeflow Spark Declarative Pipelines (the product formerly called Delta Live Tables) gives you SCD1/SCD2 as a declaration instead of a hand-rolled MERGE. The current SQL API is AUTO CDC INTO (successor to APPLY CHANGES INTO); in Python the import moved from import dlt to from pyspark import pipelines as dp.

CREATE OR REFRESH STREAMING TABLE silver.dim_customer;

CREATE FLOW customer_cdc AS AUTO CDC INTO silver.dim_customer
FROM STREAM(bronze.customer_cdc_feed)
KEYS (customer_id)
APPLY AS DELETE WHEN op_code = 'D'
SEQUENCE BY commit_ts                  -- ordering column for out-of-order events
STORED AS SCD TYPE 2
TRACK HISTORY EXCEPT (op_code, commit_ts);

What it buys you: out-of-order event handling via SEQUENCE BY, automatic __START_AT/__END_AT columns, delete and truncate handling, and no MERGE choreography to maintain. Know both: hand-rolled MERGE proves you understand the mechanics; AUTO CDC proves you know the platform direction. Be honest about which you have run in production — for you that is MERGE-based pipelines orchestrated by ADF + Databricks Jobs; SDP/AUTO CDC is lab knowledge as of mid-2026.

"Walk me through implementing SCD Type 2 on Databricks."

Strong outline: (1) clarify requirements — which attributes are tracked, latest-wins vs every-version, late data tolerance; (2) land CDC to Bronze append-only with op codes and sequence column; (3) dedupe/stage changes per key; (4) MERGE with the NULL-merge-key union pattern, attr-hash comparison, identity-column surrogate key, valid_from/valid_to/is_current; (5) mention AUTO CDC in Lakeflow Spark Declarative Pipelines as the declarative path and when you would prefer it (streaming CDC, out-of-order events); (6) close with testing — assert no overlapping windows and exactly one current row per key.

CDC ingestion patterns

SCD logic is only half the story; the other half is how change data reaches Bronze:

Data quality as a design layer

Quality is a Silver-boundary concern: data should not earn the "Silver" label without passing checks.

Frame quality in three tiers when asked: fail the pipeline (structural breaks: schema drift on a key column), quarantine the row (record-level breaks: orphan foreign keys), warn and load (soft rules: suspicious-but-valid values). Mapping rules to tiers shows judgment; "we validate everything" shows none.

Metadata-driven pipeline frameworks

Enterprises with 50+ sources do not write 50 hand-crafted pipelines. They build one parameterized framework driven by configuration: a control schema holding source connection, object list, load type (full/incremental/CDC), watermark column, target layer/table, quality rules, and schedule. A generic ADF pipeline (or Lakeflow Job) iterates the config; generic PySpark handles each load pattern. Benefits: onboarding a new table is a config row not a code deploy, standards are enforced by construction, and the audit/reconciliation framework comes free. Costs: the framework is real software — it needs versioning, testing, and an owner; and truly bespoke transforms still need escape hatches to custom code. Saying both sides is what makes the answer senior.

You have built this twice. At Maersk (Fact-Based Reporting), metadata-driven ADF + PySpark pipelines carried SAP S/4HANA, SAP AC-DOCA Universal Journal, and Hyperion HFM data into Delta on ADLS Gen2. At the Port Authority of NY & NJ, the ICMS agency-wide financial lakehouse used the same approach — metadata-driven ADF + PySpark ingestion into ADLS Gen2 Bronze from SAP ECC FI, IBM Planning Analytics, and Budget PRO, covering Plan/Budget/Forecast/Actuals for Capital and Operating portfolios across Aviation, PATH, and TB&T. The interview line: "new source objects became config entries, not new pipelines — that is how a small team scaled across an agency."

Semantic layer strategy

Gold tables are not the end of the model — someone has to own metric definitions, hierarchies, and row-level security at the consumption edge. Two dominant enterprise patterns, plus a platform-native newcomer:

DimensionPower BI semantic modelsDremio / Denodo virtualization
Where logic livesImported/DirectQuery model: DAX measures, hierarchies, RLS inside the Power BI datasetVirtual views over the lakehouse (and other sources) queried in place; SQL-defined semantics
Performance profileImport mode is extremely fast but bounded by refresh cadence and memory; DirectQuery pushes load to DBSQLQuery-time federation with acceleration (Dremio reflections); no refresh window, but hot paths need tuning
Tool lock-inMeasures usable only from Power BI/Excel ecosystemTool-agnostic: any SQL/BI client hits the same definitions
Data movementImport duplicates data into the modelNone — data stays in Delta/ADLS
Best fitMicrosoft-standard shop, curated marts, finance users living in Power BI and ExcelMany BI tools, cross-source federation, "one definition, many consumers" mandates

Watch the platform-native option too: Unity Catalog metric views (GA as of mid-2026) put governed metric definitions inside the catalog itself, queryable from SQL, AI/BI dashboards, and AI/BI Genie — a direct answer to "where do certified KPIs live so both dashboards and GenAI agents agree."

This trade-off is literally your job: at ADM you provide fit-gap and architecture recommendations including Power BI vs Dremio/Denodo semantic-layer choices, and at Maersk you delivered a Dremio + Power BI semantic-layer lakehouse (after migrating SSAS multidimensional cubes to Azure Analysis Services Tabular). When asked "import or virtualize," answer from that experience: start from consumer count and tool diversity, refresh-latency tolerance, and who owns metric definitions — then pick, and keep certified definitions in exactly one place.

Finance differentiator: modeling the Universal Journal

Generic modeling answers are table stakes; finance-domain depth is your edge. What makes finance data hard:

"How would you design a lakehouse model for Budget vs Actual reporting across multiple ERPs?"

Strong outline: (1) Bronze per source, append-only with batch lineage; (2) Silver conformed atomic facts — actuals at journal-line grain (ACDOCA-style), plans at their native grain — plus conformed dim_account, dim_entity, dim_period built with SCD2 and mapping tables that translate each ERP's chart of accounts to the group chart; (3) a version dimension unifying Actual/Plan/Budget/Forecast at a common reporting grain in Gold; (4) versioned P&L hierarchy joined at query time; (5) penny-level reconciliation gates before anything reaches the CFO; (6) ground it: "this is the R2R pattern I run at ADM across JDE, SAP, and HFM, and ran at PANYNJ across SAP ECC FI, IBM Planning Analytics, and Budget PRO."

Closing self-check for this module: can you (a) defend each medallion layer's contract in one sentence, (b) write the SCD2 MERGE from memory including the NULL-merge-key trick, (c) explain SEQUENCE BY and why out-of-order CDC breaks naive MERGE, and (d) tell the ACDOCA/variance-model story with your own systems as the example? If yes, you are interview-ready here.