Master the three-tier medallion pattern (Bronze/Silver/Gold) for retail data lakehouse design, learn systematic conversion to graph models, and implement temporal relationships with enterprise governance.
1Why Medallion Architecture?
The medallion architecture (Bronze, Silver, and Gold layers) has become the de facto standard for data lakehouse design. It aligns naturally with ShaDey's existing Conceptual (CDM), Logical (LDM), and Physical (PDM) data modeling tiers.
The Three Layers at a Glance
Bronze layers capture raw data exactly as received from source systems (SAP ECC, Salesforce, Shopify, POS terminals) with zero transformation. They are source-aligned: one Bronze table per feed.
Silver layers perform identity resolution, deduplication, type casting, and conformity rules. A single silver_customer table unifies SAP KNA1 masters, CRM accounts, and eCommerce shoppers into one golden record per business entity.
Gold layers deliver business-ready aggregations and dimensional models (facts and dimensions). A gold_fact_daily_sales table at grain store×product×date feeds executive dashboards and analyst self-service.
Bronze = PDM (Physical: exact copy from source). Silver = LDM (Logical: conformed and enriched). Gold = CDM (Conceptual: business semantics).
Real-time dashboards with batch backfill (rare in retail)
Kappa (stream only)
Streaming; event-driven
Moderate once mastered
Event log analytics, Kafka-sourced ingestion
Decision Criteria: Choose Medallion if you have scheduled batch feeds (most retail ERPs), clear source-to-target mapping, and analysts who expect dimensional models. Choose Kappa if all data flows through Kafka and you need sub-second latency. Avoid Lambda: it doubles code complexity.
Medallion is built for ShaDey's retail supply chain: SAP daily snapshots, POS nightly uploads, and Salesforce weekly syncs all land in Bronze, then flow through Silver transformation, and emerge as Gold fact tables ready for BI tools.
Medallion Architecture Three-Layer Flow: Bronze layer (amber) captures raw source data with zero transformation. Silver layer (blue) cleanses, deduplicates, and conforms data with identity resolution. Gold layer (green) creates BI-ready star schema with aggregations and dimensional models. Quality gates validate data between layers.
2Bronze Layer: Fidelity Preservation
Bronze is the raw landing zone. No transformations, no business rules, no data quality filters. One table per source feed. Columns are appended for ingestion metadata: _source_file, _ingested_at, _batch_id.
CREATE TABLEbronze_sap_vbak (
vbelnVARCHAR(10), -- Sales order number (PK in SAP)erdatDATE, -- Creation date (as received from SAP)erprioINT, -- Priority code_raw_payloadSTRING, -- Full VBAK row as JSON string_source_fileVARCHAR(255), -- e.g., s3://shadey-bronze/sap/vbak/2026-04-15.csv_ingested_atTIMESTAMP, -- UTC load time_batch_idVARCHAR(50) -- Unique batch identifier for idempotency
)
PARTITIONED BY (_ingestion_dateDATE);
Data Quality at Bronze
Schema validation only. No business rules. If SAP sends erdat = NULL, it lands as-is. If a POS field is mistyped, raw bytes are preserved.
Bronze is a compliance camera. It records exactly what arrived, unaltered, so you can replay transformations or debug data issues later.
Silver tables are cleansed, conformed, and deduplicated. Identity resolution occurs here: cross-system records are linked to a single business entity. Type casting, null handling, and date standardization are applied. One conformed table replaces N Bronze sources.
silver_customer is the golden record unifying SAP KNA1 (vendor/customer master), CRM Salesforce accounts, and Shopify shoppers into one normalized entity.
Silver DDL: Conformed Customer
CREATE TABLEsilver_customer (
customer_keyBIGINTPRIMARY KEY, -- Surrogate key, auto-generatedcustomer_id_sapVARCHAR(10), -- KNA1.KUNNR (SAP key)customer_id_crmVARCHAR(18), -- Salesforce AccountId (CRM key)customer_id_shopifyVARCHAR(50), -- Shopify customer ID (eCommerce key)nameVARCHAR(100), -- Conformed name (cleaned, standardized)emailVARCHAR(255), -- Primary email, validatedphoneVARCHAR(20), -- Phone, normalized E.164countryVARCHAR(2), -- ISO-3166 codecustomer_typeVARCHAR(50), -- 'retail' | 'wholesale' | 'partner'dbt_valid_fromTIMESTAMP, -- SCD Type 2 effective datedbt_valid_toTIMESTAMP, -- SCD Type 2 expiry date (NULL = current)dbt_updated_atTIMESTAMP, -- Last modification_dupe_scoreDECIMAL(3, 2) -- 0.0-1.0 fuzzy match confidence
) PARTITIONED BY (_effective_dateDATE);
A fuzzy matching algorithm (Levenshtein distance on names + phone) identifies duplicates across sources. Example: SAP "John Smith, 555-1234" + CRM "John Smith, (555) 123-4" → single customer_key = 42857.
Sample Silver Data
customer_key | customer_id_sap | customer_id_crm | name | email | country
--------------+-----------------+-----------------+---------------+------------------+---------
42857 | 1000085 | 001H5000001XYZQ | Elena Costa | elena@costa.com | ES
42858 | 1000086 | 001H5000001ABCD | Bob Miller | bob.m@example.com| US
42859 | NULL | NULL | Jane Doe | jane@shopify.io | GB
4Gold Layer: Semantic Clarity
Gold tables deliver business-ready aggregations and dimensional models. Mapped to ShaDey's 24-entity PDM, they organize data into facts (transactional events) and dimensions (reference data). Star schema enables rapid analyst queries.
Fact vs. Dimension
Fact tables record events: a sales transaction, an inventory movement. Each row is a business occurrence. Dimension tables describe entities: customers, products, stores, dates. They have slowly changing dimensions (SCD Type 2) to track historical changes.
CREATE TABLEgold_dim_customer (
customer_keyBIGINTPRIMARY KEY,
customer_idVARCHAR(50), -- Business keynameVARCHAR(100),
emailVARCHAR(255),
country_codeCHAR(2),
customer_segmentVARCHAR(50), -- 'platinum' | 'gold' | 'silver' | 'bronze'lifetime_valueDECIMAL(12, 2),
effective_dateDATE, -- SCD Type 2: when this version became effectiveexpiry_dateDATE, -- SCD Type 2: when this version expired (NULL = current)is_currentBOOLEAN
) PARTITIONED BY (effective_date);
Gold delivers one query, instant answer. A business analyst writes: SELECT SUM(net_revenue) FROM gold_fact_daily_sales WHERE fiscal_year = 2026 and gets retail's profit in milliseconds, no joins, no complexity.
5Cross-Layer Data Flow: End-to-End Example
Trace a single ShaDey retail transaction: a customer buys a product at a store. Watch it flow from POS terminal through all three layers.
The Journey
POS Terminal (Source): Elena Costa scans a bottle of Malbec (SKU: WINE-2024-MALBEC) at Store 42 in Madrid on 2026-04-15 09:45:32 UTC. The terminal records:
The transaction becomes one row in gold_fact_daily_sales at grain store×product×date. If Elena buys another bottle later that day, it's aggregated with this row.
Bronze preserves raw bytes; Silver resolves identity and applies data quality rules; Gold aggregates to business grain. One transaction becomes one fact row. Two transactions (same store, product, day) → one row with units_sold = 2, gross_revenue = 37.98.
6Medallion + Data Vault Coexistence
Medallion and Data Vault are not rivals. They complement each other. Bronze is a Raw Vault staging layer. Silver contains Business Vault logic and link tables. Gold is the Information Mart.
Mapping the Paradigms
Component
Data Vault Equivalent
Purpose
Bronze
Raw Vault staging + load history
Fidelity to source; audit trail
Silver
Business Vault + Link tables
Identity resolution; relationship capture
Gold
Information Mart
Business semantics; dimensional model
Example: SAP VBAK (sales order header) lands in bronze_sap_vbak. Silver creates a silver_link_customer_order table to capture the relationship between customer and order. Gold materializes gold_fact_daily_sales which pre-joins all dimensions for BI tool consumption.
Use both paradigms in one platform. Bronze (Raw Vault) loads fast. Silver (Business Vault) resolves identity. Gold (Information Mart) delivers speed. No conflict; layers of increasing business value.
7Relational → Graph Conversion Methodology
Graph databases excel at traversal: "Show me all products Elena bought, sorted by date." Relational tables must be systematically converted to graph nodes and edges. A methodical approach ensures no semantic loss.
Four-Step Conversion Framework
Relational-to-Graph Conversion: Left side shows normalized relational tables (DIM_CUSTOMER, FACT_SALES, DIM_PRODUCT, DIM_STORE) with foreign key relationships. Right side shows converted graph model with nodes (Customer, Product, Store) and edges ([:PURCHASED], [:SOLD_AT]) carrying properties. Four-step systematic conversion ensures no semantic loss.
Step 1: Identify Nouns (Entities) → Nodes
Every table with identity is a node type. ShaDey's relational model yields:
Customer from DIM_CUSTOMER
Product from DIM_PRODUCT
Order from FACT_SALES
Store from DIM_STORE
Inventory from FACT_INVENTORY
Step 2: Identify Verbs (Relationships) → Edges
Every foreign key or association becomes an edge type.
Every relational entity and relationship has a graph equivalent. The conversion is systematic and loss-less. Graph queries enable traversals impossible in SQL (e.g., "Show me customers in Spain who bought wine, plus their purchase history").
8Temporal Relationships in Graph
Graphs excel at expressing relationships that change over time. ShaDey must model static relationships (a product belongs to a category) and temporal relationships (a customer purchased a product on a specific date with a specific amount).
Three Types of Relationships
1. Static Edges: Permanently True
(:Customer)-[:LIVES_IN]→(:City): If Elena lives in Madrid, this relationship is stable (unless she moves, handled separately).
2. Temporal Edges: Time-Stamped Events
(:Customer)-[:PURCHASED {date: '2026-01-15', amount: 47.50}]→(:Product): Properties capture the event's context. Elena purchased Malbec on Jan 15 for €47.50.
3. Versioned Edges: Period-Valid Relationships
(:Employee)-[:REPORTS_TO {from: '2025-01-01', to: '2025-12-31'}]→(:Manager): SCD Type 2 in graph form. One edge per tenure period.
Versioned: Employee Reports-To (SCD Type 2 Pattern)
-- Employee Elena reports to Manager Bob (2025 tenure)CREATE (elena_emp:Employee {id: 'E123', name: 'Elena'})
CREATE (bob_mgr_2025:Manager {id: 'M456', name: 'Bob', tenure_start: '2025-01-01', tenure_end: '2025-12-31'})
CREATE (elena_emp) -[:REPORTS_TO {
from: '2025-01-01',
to: '2025-12-31'
}]-> (bob_mgr_2025)
Cypher Temporal Queries
Query 1: Elena's Purchase History (Ordered by Date)
MATCH (elena:Customer {name: 'Elena'})
-[:PURCHASED]-> (product:Product)
RETURNproduct.name, product.skuORDER BYpurchased_atDESC
Query 2: Who Was Store Manager in Q3 2025?
MATCH (store:Store {id: 'S42'})
-[:MANAGED_BY]-> (mgr:Manager)
WHEREmgr.tenure_start<='2025-07-01'ANDmgr.tenure_end>='2025-09-30'RETURNmgr.name
Query 3: Customers Who Purchased A Then B Within 30 Days
MATCH (cust:Customer)
-[r1:PURCHASED]-> (prodA:Product {sku: 'WINE-2024'})
MATCH (cust)
-[r2:PURCHASED]-> (prodB:Product {sku: 'VODKA-2024'})
WITHcust, r1.purchased_atASdate_a, r2.purchased_atASdate_bWHEREduration.between(date_a, date_b).days<= 30
ANDdate_b>date_aRETURNcust.name, date_a, date_b
Point-in-Time Snapshots vs. Temporal Properties
Approach
Pros
Cons
Snapshot (Versioned Nodes)
Simple queries; clear versions
Duplicate nodes per period
Temporal Properties (Time-Stamped Edges)
One node; edge dates; compact
Complex range queries
Use temporal properties (timestamps on edges) for event data (purchases, inventory movements). Use versioned nodes for master data (product prices, employee roles). Hybrid approach maximizes query flexibility.
9Relational + Graph Coexistence Without Duplication
ShaDey runs star schema in Snowflake for aggregation and graph in Neo4j for traversal. The trick is preventing duplicate business logic. Sync strategies ensure both systems stay in agreement.
The Dual-Model Discipline
Relational (Snowflake) wins for: "What are total sales by category this quarter?" (sum, group by, many rows to aggregate).
Graph (Neo4j) wins for: "Show me Elena's purchase history, plus products she bought alongside them" (traversal, relationships, pattern matching).
Decision Matrix: Which Query Goes Where?
Query Pattern
Relational
Graph
Aggregate (SUM, COUNT, AVG)
✓ Fast
Traversal (N hops, variable depth)
✓ Fast
Pattern matching (e.g., A→B→C)
✓ Fast
Fact table join (star schema)
✓ Optimized
Graph analytics (centrality, community)
✓ Native
Data Sync Pipeline Design
Relational → Graph (CDC-Based): Firestore logs Bronze/Silver table changes. A Kafka consumer publishes changes to Neo4j via transactional writes. Schema matches DDL in Section 7.
Graph → Relational (Materialized Views): Neo4j exports aggregated results (e.g., customer centrality scores) as CSV files. Snowflake materializes into a gold_customer_graph_metrics table.
Use CDC (Change Data Capture) to sync relational changes to graph. Implement idempotent upserts in Neo4j keyed by business ID (customer_id, product_sku). Avoid dual-write patterns; order of consistency is: Relational (source of truth) → Graph (derived).
10Enterprise Model Governance
ShaDey's data models (CDM, LDM, PDM) must be governed as living artifacts. Forward engineering (model → DDL) and reverse engineering (DDL → model) keep documents and databases in sync.
Governance Workflow
Propose: A data engineer proposes a new column in DIM_CUSTOMER: loyalty_tier VARCHAR(50).
Review: The data governance board reviews impact: which reports use DIM_CUSTOMER? Which ETL jobs need updates?
Approve: If impact is acceptable, the change is approved and scheduled.
Deploy: The DDL is applied to Snowflake. The model document is updated to reflect the change.
Forward Engineering: Model → DDL
Start with the logical model (ERD). Tool (e.g., dbt, Liquibase) generates DDL automatically.
Model document and live schema must match. Version both. Review all changes through the governance board. Use automated drift detection to flag discrepancies. This prevents "shadow IT" schemas.
11Semantic Drift Detection
Semantic drift occurs when a column's real-world meaning changes but the schema doesn't. customer_type originally means "retail/wholesale" but gradually gets overloaded with "internal/external". This breaks reports silently.
What Is Semantic Drift?
Example: In 2023, customer_type in DIM_CUSTOMER had exactly 2 values: 'Retail', 'Wholesale'. By 2026, due to ad-hoc fixes, it has 5 values: 'Retail', 'Wholesale', 'Internal', 'Partner', 'API'. Reports filtering on customer_type = 'Retail' now miss Partner customers who should be excluded.
Detection Methods
1. Data Profiling
Run periodic profiling on all columns. Alert on domain changes (new unexpected values).
Trace a column's origin. If gold_dim_customer.customer_type historically came from silver_customer.customer_type (which came from SAP KNA1.KTOKD), check if the source domain changed.
3. Report Impact Analysis
Which reports reference customer_type`? Are they still accurate?
Problem: LOYALTY_REPORT_v2 filters on customer_type = 'Retail' to calculate "core retail customer revenue". However, by Q2 2026, the customer_type` column had 5 undocumented values. The report was silently excluding 'Partner' customers, causing an undercount of ~8% revenue.
Solution: Automated quarterly profiling detected the domain expansion. A data engineer was assigned to document the new values and update filters in LOYALTY_REPORT_v2.
Governance Metrics
Metric
Q1 2026
Q2 2026
Target
Columns with drift detected
0
3
< 5 per quarter
Days to resolve drift
N/A
18 days avg
< 7 days
Reports affected by unresolved drift
0
1
0
Profile all columns monthly. Alert on domain expansion (new values). Trace lineage for each drift alert. Update documentation and reports synchronously. Establish SLAs for drift resolution (7 days to document or revert).
12Neo4j for Full Lakehouse Lineage
Model the entire medallion architecture (layers, tables, columns, transformations) as a graph in Neo4j. Then ask graph questions: "What Gold tables are affected if bronze_sap_vbak schema changes?"
Query 3: All Transformations Between Silver and Gold
MATCH (silver_t:Table)
-[flow:FLOWS_TO]-> (gold_t:Table)
WHERE
(silver_t) -[:CONTAINS]<- (sl:Layer {name: 'Silver'})
AND (gold_t) -[:CONTAINS]<- (gl:Layer {name: 'Gold'})
RETURNsilver_t.name, flow.via, gold_t.name
Graph Visualization
The Neo4j browser renders the lineage as an interactive graph. Colors represent layers: Bronze (amber), Silver (blue), Gold (yellow). Edge labels show dbt transformation names.
One Cypher query discovers all downstream impact. A schema change in bronze_sap_vbak is tracked forward through transformations to all Gold tables, enabling safe change impact analysis. No blind spots.