Home / Databricks on Azure

Databricks on AzureHow-toAdvanced

A Delta lakehouse blueprint for Azure Databricks: catalogs, medallion layers, loading patterns and data products

A reusable design for a Delta lakehouse on Azure Databricks. Covers how to lay out Unity Catalog catalogs and schemas, what belongs in bronze, silver and gold, standard loading patterns (append, MERGE, SCD2, snapshot), table settings, data quality, compute per workload, and how to publish gold tables as governed data products.

Deepak Bhardwaj7 min read

Applies toAzure Databricks Premium with Unity Catalog, ADLS Gen2, Databricks Runtime 14.3 LTS and later

Most lakehouse problems aren't Spark problems. They're design problems: no agreed layout, every team loading data differently, and nobody sure which table is "the real one". This blueprint gives the decisions to make once, so every pipeline follows the same pattern.

1. The big picture

Sources ──> Landing (files) ──> Bronze ──> Silver ──> Gold ──> Consumers
            ADF / Lakeflow      raw        clean      business   Power BI, SQL,
            Connect / Kafka     history    conformed  products   ML, Delta Sharing
Layer Purpose Shape Who reads it
Landing Files exactly as received Files in a volume Pipelines only
Bronze Raw records + load metadata, full history Source shape, append-only Data engineers
Silver Cleaned, typed, de-duplicated, conformed keys Entity tables (customer, order) Engineers, data scientists
Gold Business-ready, aggregated or dimensional Star schema, KPIs, features Analysts, BI, apps

2. Unity Catalog layout

Choose one convention and enforce it. Two that work well:

A. Catalog per environment + domain, schema per layer (most common):

dev_sales.bronze      test_sales.bronze      prod_sales.bronze
dev_sales.silver      test_sales.silver      prod_sales.silver
dev_sales.gold        test_sales.gold        prod_sales.gold

B. Catalog per environment + layer, schema per domain: prod_silver.sales, prod_silver.finance.

Pattern A keeps domain ownership clear: one owner group per catalog. Pattern B suits a central platform team.

Whichever you choose:

  • Workspace-catalog binding: prod_* catalogs are only bound to prod workspaces.
  • Managed location per catalog, each in its own ADLS container, for isolation and cost tracking.
  • Owner = a group, for example sales-data-owners. Pipelines write as a service principal.
  • Shared reference data (calendar, currency, org hierarchy) goes in a prod_shared catalog.

Naming

Object Convention Example
Bronze table source_entity sap_vbak, crm_contacts
Silver table business entity, singular or plural (pick one) customers, orders
Gold dimension / fact dim_ / fact_ dim_customer, fact_sales
Gold aggregate agg_ + grain agg_sales_daily
Technical columns _ prefix _ingest_ts, _source_file, _batch_id

3. Table standards

Apply them with a template or in a shared library (see the Python wheel post):

CREATE TABLE prod_sales.silver.orders (
  order_id      STRING NOT NULL,
  customer_id   STRING,
  order_ts      TIMESTAMP,
  amount        DECIMAL(18,2),
  status        STRING,
  _ingest_ts    TIMESTAMP,
  _batch_id     STRING,
  CONSTRAINT orders_pk PRIMARY KEY (order_id)          -- informational, helps BI and the optimizer
)
CLUSTER BY (order_ts, customer_id)                      -- liquid clustering on common filters
COMMENT 'One row per sales order; latest state. Source: SAP VBAK/VBAP.'
TBLPROPERTIES ('delta.enableChangeDataFeed' = 'true');

ALTER TABLE prod_sales.silver.orders ADD CONSTRAINT valid_amount CHECK (amount >= 0);
  • Managed tables by default. Enable predictive optimization so OPTIMIZE and VACUUM run automatically.
  • Liquid clustering instead of partitioning for most tables. Partition only very large tables with a natural, low-cardinality key such as date, and only if you need it.
  • Comments on tables and key columns. They feed search, AI/BI Genie and documentation.
  • Change Data Feed on silver tables that feed incremental gold.
  • Tags for classification (pii, confidential) to drive masking and policies.

4. Standard loading patterns

Give every pipeline one of these five patterns. Don't invent new ones.

Pattern Use for How
Append Events, logs, bronze Auto Loader / streaming append; de-duplicate on a key within a watermark
Upsert (SCD1) Latest state of an entity MERGE on business key, newest record wins (sequence column)
SCD Type 2 History of attribute changes AUTO CDC ... STORED AS SCD TYPE 2 in a Lakeflow pipeline, or MERGE with valid_from / valid_to / is_current
Snapshot replace Small reference tables, full extracts CREATE OR REPLACE TABLE ... AS SELECT, or INSERT OVERWRITE
Partition replace Recomputing a time window in gold INSERT INTO ... REPLACE WHERE order_date >= '2026-10-01'
-- partition replace: recompute the last 3 days idempotently
INSERT INTO prod_sales.gold.agg_sales_daily
REPLACE WHERE order_date >= current_date() - 3
SELECT to_date(order_ts) AS order_date, region, count(*) AS orders, sum(amount) AS revenue
FROM prod_sales.silver.orders o JOIN prod_sales.silver.customers c USING (customer_id)
WHERE to_date(order_ts) >= current_date() - 3
GROUP BY ALL;

Every load must be idempotent. Rerunning the same batch gives the same result. Then retries and backfills are safe.

5. Data quality

Layer Checks On failure
Bronze File arrived, schema readable, row count > 0 Alert; keep loading (rescue bad records)
Silver Not-null keys, valid types and ranges, referential checks, duplicates Quarantine bad rows; fail if above a threshold
Gold Reconciliation to silver/source totals, freshness Fail the publish; keep the last good version

In Lakeflow declarative pipelines, use expectations:

CONSTRAINT valid_key    EXPECT (order_id IS NOT NULL)  ON VIOLATION DROP ROW,
CONSTRAINT valid_amount EXPECT (amount >= 0)           ON VIOLATION FAIL UPDATE

For continuous checks such as freshness and anomalies, add data quality monitoring (Lakehouse Monitoring) on key gold tables.

6. Compute per workload

Workload Compute
Scheduled pipelines Lakeflow jobs on job compute (policies) or serverless jobs
Declarative pipelines Serverless pipelines where allowed
BI and SQL Serverless SQL warehouses, separate for BI vs ad hoc
Data science Dedicated (single-user) clusters, ML runtime
Development Small personal compute with auto-termination

7. Gold as data products

A gold table is a product with an owner and a contract, not just a table.

  • Owner group and support channel in the table comment or tags.
  • Documented grain, keys and refresh schedule (SLA).
  • Stable schema. Breaking changes go to a new table or version (fact_sales_v2) with a deprecation period.
  • Access granted to consumer groups; row filters and masks where needed.
  • Shared outside the platform with Delta Sharing, not file exports.
  • Certified with tags (for example certified=true), so users know which tables to trust.

8. Operations

  • Lineage comes automatically in Unity Catalog. Use it for impact analysis before changing silver tables.
  • System tables for cost per catalog and job, and for failed runs (see the monitoring post).
  • Time travel retention of at least 7 days on silver and gold; deep clone backups for critical tables.
  • CI/CD with Asset Bundles: dev, then test, then prod, with the same code and different catalog names passed as variables.
# bundle variable makes the same pipeline target dev_sales / prod_sales
variables:
  catalog:
    default: dev_sales
targets:
  prod:
    variables:
      catalog: prod_sales

Anti-patterns

Anti-pattern Why it hurts
BI reading bronze or silver directly Every report re-implements business logic differently
One huge "data lake" catalog with personal schemas No ownership, no isolation, permission sprawl
Over-partitioning (by ID or hour) Millions of tiny files; slow reads
Non-idempotent appends in silver/gold Duplicates on every retry
Hard-coded catalog names in code Can't promote between environments
Gold tables without owners Nobody fixes them when they break

Tip

Write these decisions down as a one-page lakehouse standard in your repository: layout, naming, the five loading patterns and the data-product checklist. Review new pipelines against it in pull requests. Consistency matters more than any single choice.