Skip to content

Data Architecture

Purpose

Data Architecture is the highest-authority structural artifact for data pipeline design in the Design activity. Its unique job is to describe the durable pipeline shape: ingestion patterns, medallion layer topology, streaming vs. batch semantics, transformation patterns, governance boundaries, quality gates, and critical performance or cost tradeoffs.

Data Architecture is not a data model (captured in Data Design), implementation plan, or ADR. It is the bridge between PRD (kind: data) (requirements) and implementation: “given these requirements, here is how the pipeline is structured.”

Example

Show a worked example of this artifact
---
ddx:
  id: example.data-architecture.customer-360
  authoring:
    home: repo
  # Previous depends_on: example.data-prd.customer-360 — dropped when
  # data-prd collapsed into prd as kind: data variant (ADR-008). No
  # equivalent example.prd.customer-360 is yet published.
---

# Data Architecture: Customer-360 Analytics

## Overview

Customer-360 joins Salesforce accounts and opportunities with Stripe customers,
subscriptions, invoices, and charges into one Databricks Lakehouse so that sales
and finance can query revenue, subscription health, and account status from a
single set of Gold tables. Today the two systems are reconciled by hand in
spreadsheets; this pipeline replaces that with a daily medallion load whose
reconciliation quality is measured and enforced. Entity-level modelling lives in
[[data-design]]; column-level rules live in [[data-quality-expectations]].

### Scope

This architecture covers the Customer-360 medallion pipeline: daily batch
ingestion of Salesforce accounts and opportunities and Stripe customers,
subscriptions, invoices, and charges into a Databricks Lakehouse. It includes
Bronze raw-layer storage, Silver reconciliation and deduplication, and Gold
fact and dimension tables for analytics queries. Historical loads of 12 months
are supported; incremental daily loads begin in week 2. Streaming ingestion, ML
training stores, and external data warehouse federation are outside v1 scope.
The driving requirements are 24-hour freshness for sales dashboards, a
Salesforce-to-Stripe reconciliation rate of at least 98%, and a compute budget
of at most 500 USD per month.

### System Context

| External System | Role | Protocol | Data Volume |
|-----------------|------|----------|-------------|
| Salesforce | Source of accounts, opportunities, ownership | HTTPS REST API; daily full export | Tens of thousands of accounts; ~5k opportunities per month |
| Stripe | Source of customers, subscriptions, invoices, charges | HTTPS REST API; daily full export (webhook in v2) | ~50k invoices and charges per month |
| Databricks Lakehouse | Medallion storage and compute for ingestion and queries | Databricks SQL; PySpark jobs | Four scheduled jobs per day |
| BI tool (Tableau or Sigma) | Sales and finance dashboards querying Gold | Databricks SQL via ODBC | ~200 dashboard queries per day |
| Data Engineer | Orchestrates jobs, monitors SLAs, maintains schemas | Databricks Workflows, notebooks | Daily monitoring |

```mermaid
graph TB
    SF[Salesforce<br/>Accounts + Opps] -->|HTTPS API<br/>Daily export| DBX[Databricks Lakehouse<br/>Bronze + Silver + Gold]
    Stripe[Stripe<br/>Customers + Subscriptions<br/>+ Invoices + Charges] -->|HTTPS API<br/>Daily export| DBX
    DBX -->|Databricks SQL| BI[BI Tool<br/>Sales Dashboards]
    DBX -->|Databricks SQL| DE[Data Engineer<br/>Monitoring]
```

## Medallion Topology

### Layer Strategy

Three layers, all refreshed by daily batch. Bronze preserves each source export
verbatim so any day can be replayed; Silver reconciles Salesforce to Stripe,
deduplicates, and hashes PII so downstream consumers never see raw identifiers;
Gold serves business-ready facts and dimensions to BI. Daily batch satisfies the
24-hour freshness requirement, and keeping reconciliation in Silver lets the
98% match target be measured once per load rather than per dashboard.

### Bronze Layer (Raw Ingestion)

- **Purpose**: Land each daily export exactly as received, organised by source
  system and entity.
- **Source integration pattern**: Scheduled REST export written to the landing
  path, picked up by Auto Loader in triggered mode once per day.
- **Schema handling**: Strict against the registered source schema; rows with
  unexpected columns are quarantined and a schema change needs manual approval.
- **Retention policy**: 90 days, enough to replay a full quarter of loads while
  keeping raw PII exposure short.

| Table | Source | Partitioning | Retention | Notes |
|-------|--------|--------------|-----------|-------|
| bronze.salesforce_accounts | Salesforce API | date_loaded | 90 days | Full daily export; preserves all fields |
| bronze.salesforce_opportunities | Salesforce API | date_loaded | 90 days | Full daily export; includes closed_date |
| bronze.stripe_customers | Stripe API | date_loaded | 90 days | Full daily export; includes metadata tags |
| bronze.stripe_subscriptions | Stripe API | date_loaded | 90 days | Full daily export; includes status changes |
| bronze.stripe_invoices | Stripe API | date_loaded | 90 days | Full daily export; raw line items |
| bronze.stripe_charges | Stripe API | date_loaded | 90 days | Full daily export; includes payment outcomes |

Responsibilities: ingest every record from the export, preserve source schema
exactly, tag rows with `date_loaded` and source-system identifier, quarantine
rows that fail schema validation.

Quality gates: ingest metadata present on every row, no column truncation,
daily row count at least 95% of the prior day, export watchdog fires if a source
has not landed by 02:00 UTC.

### Silver Layer (Validated and Transformed)

- **Purpose**: Cleaned, deduplicated, reconciled data with lineage and quality
  flags.
- **Deduplication strategy**: Stripe subscription events on
  (`subscription_id`, `event_date`), latest `date_loaded` wins; charges on
  `charge_id`.
- **Type coercion / null policy**: Amounts cast to DECIMAL(18,2); rows with
  null required columns are rejected to quarantine, not defaulted.
- **Referential integrity**: Every payment transaction must resolve to a
  subscription and an invoice; unmatched Salesforce accounts keep a flagged
  `dim_customer` row rather than being dropped.

| Table | Source(s) | Partitioning | Retention | Key Transformations |
|-------|-----------|--------------|-----------|---------------------|
| silver.dim_customer | bronze.salesforce_accounts + bronze.stripe_customers | customer_id | 3 years | 1:1 Salesforce-to-Stripe match via email; hash PII; null-check on account names |
| silver.dim_date | Calendar | date_key | 5 years | Standard calendar table; fiscal month, quarter, year |
| silver.fct_subscription_event | bronze.stripe_subscriptions | subscription_id, event_date | 3 years | Deduplicate on Stripe subscription id; flag late-arriving rows; join to dim_customer |
| silver.fct_payment_transaction | bronze.stripe_charges + bronze.stripe_invoices | charge_id, payment_date | 3 years | Flatten invoice line items; join charge to invoice and subscription; hash card brand |
| silver.reconciliation_log | Derived | load_date | 90 days | Count of matched and unmatched pairs per load; reconciliation confidence scores |

Join strategy (pipeline level; entity-level joins live in [[data-design]]):

| Join | Source Layers | Type | Cardinality | Latency Impact |
|------|---------------|------|-------------|----------------|
| Salesforce account to Stripe customer | Bronze accounts / Bronze customers | Outer, unmatched flagged | 1:1 | Adds ~10 minutes for fuzzy email normalisation |
| Charge to invoice to subscription | Bronze charges / Bronze invoices / Silver subscription events | Inner | N:1 | Low; keyed joins |
| Facts to dim_customer | Silver facts / Silver dim_customer | Inner | N:1 | Low |

Quality gates: PK uniqueness on every Silver table, NOT NULL on critical
columns, reconciliation rate at least 98%, no raw email or phone outside
`dim_customer.email_hash`, row-count reconciliation with Bronze within
tolerance.

### Gold Layer (Consumption)

- **Purpose**: Business-ready tables optimised for dashboard queries.
- **Optimisation strategy**: Partition by `customer_id` and period; Z-order on
  `year_month` and `as_of_date`; no materialised views in v1 because query
  volume is low.
- **Retention policy**: 3 years, matching the finance reporting horizon.

| Table | Use Case | Consumers | Freshness Target |
|-------|----------|-----------|------------------|
| gold.fct_monthly_revenue | Sales forecasting, revenue metrics; 1 row per customer per month | Sales analyst, Finance | Available by 07:00 UTC daily |
| gold.fct_subscription_health | Churn risk scoring, subscription metrics; 1 row per subscription | Sales analyst, Customer success | Available by 07:00 UTC daily |
| gold.dim_customer_account | Account overview and drill-down; 1 row per customer | Sales analyst, BI dashboards | Available by 07:00 UTC daily |

Computations:

- `fct_monthly_revenue`: sums paid invoices grouped by customer and calendar
  month; includes subscription state.
- `fct_subscription_health`: latest subscription status, months active, failed
  payment count, ageing of unpaid invoices.
- `dim_customer_account`: joins Salesforce account attributes with current
  Stripe subscription status.

Quality gates: aggregate revenue reconciles with Silver within 0.01%, customer
cardinality equals Silver, every subscription in Gold exists in Silver, release
by 07:00 UTC.

## Data Flow

Both sources export at 22:00 UTC. Bronze lands by 02:00, Silver reconciliation
finishes by 05:00, and Gold is released to BI by 07:00. Each layer's load is a
separate workflow task gated on the previous layer's quality checks.

```mermaid
sequenceDiagram
    participant SF as Salesforce API
    participant Stripe as Stripe API
    participant DBX as Databricks
    participant Bronze as Bronze Tables
    participant Silver as Silver Tables
    participant Gold as Gold Tables
    participant BI as BI Tool

    SF->>DBX: Daily export (accounts, opps)
    Stripe->>DBX: Daily export (customers, subs, invoices, charges)
    DBX->>Bronze: Land raw data; validate schema and completeness
    Note over DBX: Reconciliation: match Salesforce-Stripe via email
    Bronze->>Silver: Deduplicate, hash PII, join and flag late arrivals
    Note over Silver: Check reconciliation accuracy (98% threshold)
    Silver->>Gold: Aggregate facts and dimensions
    Gold->>BI: SQL query for dashboards
    Note over BI: Sales forecast, churn alerts, AR aging
```

### Incremental vs Full Refresh

- **Bronze**: Full daily export appended as a new `date_loaded` partition; a
  failed day is replayed by overwriting that partition.
- **Silver**: `dim_customer` is fully recomputed each day from the latest Bronze
  partition because matching depends on the whole population; fact tables are
  MERGEd on their natural keys so late-arriving rows update in place.
- **Gold**: The current month is rebuilt from Silver each day; closed months
  are append-only and recomputed only on an explicit backfill.

## Processing Semantics

### Streaming vs Batch Decision

| Layer | Strategy | Rationale | SLA Implication |
|-------|----------|-----------|-----------------|
| Bronze | Batch (daily, triggered Auto Loader) | Both sources offer a daily full export; Stripe webhooks would add two weeks of integration work for no v1 consumer need | Bronze complete by 02:00 UTC |
| Silver | Batch (daily) | Reconciliation needs the full day's population; per-row streaming would recompute matches repeatedly | Silver complete by 05:00 UTC |
| Gold | Batch (daily) | Dashboards are reviewed in the morning; sub-day freshness is not a requirement | Gold released by 07:00 UTC |

### Processing Framework

- **Framework**: PySpark on job clusters for Bronze ingestion; Databricks SQL
  for Silver and Gold transformations.
- **Orchestration**: One Databricks Workflow with four dependent tasks
  (Salesforce export, Stripe export, Silver load, Gold load).
- **Failure handling**: Each task retries once after five minutes; a second
  failure holds all downstream tasks and pages the on-call data engineer.
- **Idempotence / exactly-once posture**: Bronze overwrites its `date_loaded`
  partition; Silver facts MERGE on natural keys; Gold overwrites the affected
  month. Any task can be rerun for a given day without duplicating rows.
- **Schema evolution policy**: Strict. New source columns land in quarantine
  until a data engineer approves the schema change and updates Silver.

### Latency and Throughput Targets

| Stage | Latency Target | Throughput Target | Binding Constraint |
|-------|----------------|-------------------|--------------------|
| Source → Bronze | Complete by 02:00 UTC (4 h after export) | Full export, tens of thousands of rows per source per day | Salesforce and Stripe API rate limits on the export job |
| Bronze → Silver | Complete by 05:00 UTC (3 h) | Full recompute of dim_customer; incremental facts | Fuzzy email matching cost |
| Silver → Gold | Complete by 07:00 UTC (2 h) | Rebuild current month; append closed months | Monthly aggregation over 3 years of facts |

## Pipeline-Level Quality Contracts

The pipeline enforces the following contracts at each layer boundary. The
executable `EXPECT` clauses and SQL assertions (BE-, SE-, GE-, and CL-series)
live in [[data-quality-expectations]].

### Bronze → Silver

- **Schema contract**: Every Bronze table matches its registered source schema;
  Silver refuses to start on a schema mismatch.
- **Volume contract**: Today's row count is at least 95% of the prior day's for
  every Bronze table.
- **Freshness contract**: All six Bronze tables have landed for today's
  `date_loaded` by 02:00 UTC; otherwise Silver is held.
- **Violation handling**: Hold Silver, page on-call, quarantine the partition.

### Silver → Gold

- **Uniqueness contract**: `customer_id` unique in `dim_customer`;
  (`subscription_id`, `event_date`) unique in `fct_subscription_event`;
  `charge_id` unique in `fct_payment_transaction`.
- **Referential contract**: Every payment transaction resolves to a subscription
  and an invoice; every fact row resolves to a `dim_customer` row.
- **Aggregate-reconciliation contract**: Monthly revenue summed in Gold equals
  paid transaction amounts summed in Silver within 0.01%.
- **Violation handling**: Reject the Gold refresh, keep yesterday's Gold live,
  alert on-call and Finance for revenue mismatches.

### Cross-Layer Contracts

| Contract | Assertion | If Violated |
|----------|-----------|-------------|
| Reconciliation rate Bronze → Silver | At least 98% of Salesforce accounts matched to a Stripe customer | Block Gold; investigate matching logic |
| Customer cardinality Silver → Gold | `dim_customer_account` row count equals `dim_customer` row count | Reject Gold until reconciled |
| Subscription lineage Gold → Silver | No `subscription_id` in Gold without a Silver subscription event | Quarantine the Gold rows and alert |
| PII containment | No raw email or phone outside `dim_customer.email_hash` in Silver or Gold | Halt pipeline; compliance review |

## Governance and Access Control

### Identity and Access Model

| Role | Catalog Scope | Layer Access | Permissions |
|------|---------------|--------------|-------------|
| Data engineer | `main.customer_360_*` | Bronze, Silver, Gold | SELECT, MODIFY, EXECUTE on workflows |
| Sales and finance analyst | `main.customer_360_gold` | Gold | SELECT |
| BI service principal | `main.customer_360_gold` | Gold | SELECT via dynamic views |
| Compliance reviewer | `main.customer_360_silver` | Silver (masked) | SELECT on masked views |

### Data Classification and Retention

| Layer | Classification | Sensitive Categories | Retention Policy | Masking Policy |
|-------|----------------|----------------------|------------------|----------------|
| Bronze | Restricted | Customer contact details, billing identifiers, card brand | 90 days then deleted | Raw; data engineers only |
| Silver | Confidential | Hashed contact identifiers, hashed card brand | 3 years (VACUUM after retention) | Hash only; compliance sees masked views |
| Gold | Internal | Aggregated revenue and subscription state per customer | 3 years | Default masking of account owner email for BI |

### Fine-Grained Access Control

- **Row-level security**: None in v1; the workspace serves a single company and
  all analysts may see all customers.
- **Column-level security**: Contact and billing identifiers are hashed in
  Silver; no raw PII exists beyond Bronze, and Bronze is limited to data
  engineers.
- **Dynamic views**: BI reads Gold through dynamic views that mask the account
  owner email unless the caller is in the sales-ops group.

## Platform Design

### Catalog Organisation

```
main
├── customer_360_bronze
│   ├── salesforce_accounts, salesforce_opportunities
│   └── stripe_customers, stripe_subscriptions, stripe_invoices, stripe_charges
├── customer_360_silver
│   ├── dim_customer, dim_date
│   └── fct_subscription_event, fct_payment_transaction, reconciliation_log
├── customer_360_gold
│   ├── fct_monthly_revenue, fct_subscription_health
│   └── dim_customer_account, data_quality_log, data_quality_dashboard
└── customer_360_quarantine
    └── rejected rows by date_loaded
```

### Compute Strategy

| Workload | Compute Tier | Sizing Approach | Rationale |
|----------|--------------|-----------------|-----------|
| Bronze ingestion (two export jobs) | Job cluster, 2 workers | Fixed size; runs once per day | Export volume is predictable; no auto-scaling needed |
| Silver transformation | Job cluster, 2 workers | Fixed size | Reconciliation is bounded by the daily population |
| Gold consumption | Serverless SQL warehouse, small | Auto-stop after 10 minutes idle | Morning dashboard bursts; idle most of the day |

Cost-shaping levers: fixed job clusters with auto-termination, serverless
warehouse auto-stop, partition pruning on `date_loaded` and `year_month`, and
no materialised views until query volume justifies them. The PRD budget
ceiling is 500 USD per month; the current estimate is roughly 960 DBU per
month for jobs plus a small ad-hoc query allowance, tracked in the monthly
billing dashboard.

### Storage Strategy

| Layer | Format | Partitioning | Clustering / Optimisation |
|-------|--------|--------------|---------------------------|
| Bronze | Delta at `s3://main-catalog/customer_360_bronze/` | date_loaded | Weekly OPTIMIZE; VACUUM after 90 days |
| Silver | Delta at `s3://main-catalog/customer_360_silver/` | customer_id, event or payment date | Z-order on customer_id; VACUUM after 3 years |
| Gold | Delta at `s3://main-catalog/customer_360_gold/` | customer_id, year_month or as_of_date | Z-order on year_month; VACUUM after 3 years |

### Platform Features in Use

| Feature | Use Case | Configuration Note |
|---------|----------|--------------------|
| Auto Loader | Bronze ingestion from the export landing path | Triggered once per day; schema mode strict with quarantine |
| Databricks SQL streaming tables with EXPECT | Silver row-level quality checks | Daily trigger; ON VIOLATION DROP ROW to quarantine |
| Databricks Workflows | End-to-end refresh | Four tasks with dependencies; one retry; page on second failure |
| Unity Catalog | Access control, lineage, masking | Dynamic views for BI; audit logs enabled on all three schemas |

For non-Databricks platforms, see
`docs/resources/databricks-platform-substitution.md` for the equivalent terms.

## Decisions and Tradeoffs

### Key Architecture Decisions

| Decision | Choice | Rationale | Alternative Considered | Consequence |
|----------|--------|-----------|------------------------|-------------|
| Ingestion cadence | Daily batch | Both sources offer daily full exports; sales SLA accepts 24-hour latency; batch loads validate fully and replay cleanly | Stripe webhooks streaming into Bronze | No real-time churn alerts in v1; easier replay of failed days |
| Layer isolation | Separate Bronze, Silver, Gold schemas | PII isolation, per-layer access control, backfill one layer without reprocessing others | Single schema with layer prefixes | More tables to document; needs naming discipline |
| Customer matching | Email match with fuzzy normalisation in Silver | Email is the most reliable cross-system identifier | Manual mapping table maintained by sales ops | Not 100% accurate; unmatched accounts flagged for manual linking |
| Invoice line items | Flatten into Silver payment transactions | Simplifies Gold aggregations; avoids multi-row-per-invoice joins | Keep line items normalised until Gold | Denormalised Silver, acceptable for analytics use |
| Card data | Hash card brand only; never store card tokens | PCI scope stays out of the lakehouse | Store tokenised card references | Metrics cannot distinguish issuer; acceptable for v1 |

### Performance vs Cost Tradeoffs

- Daily batch instead of streaming trades sub-day freshness for roughly half
  the compute cost and a far simpler replay story.
- Rebuilding the current Gold month each day trades a few minutes of compute
  for exact aggregates without incremental-merge edge cases.
- A serverless SQL warehouse for BI trades slightly higher per-query cost for
  zero idle cost outside the morning dashboard window.
- Fixed job clusters trade elasticity for predictable spend inside the
  500 USD monthly ceiling.

### Known Risks and Mitigations

| Risk | Mitigation |
|------|------------|
| Salesforce or Stripe export fails or is late | Export watchdog at 02:00 UTC; one retry; page on-call; Silver held rather than run on a partial day |
| Reconciliation rate drops below 98% after a CRM data change | SE-001 blocks Gold; reconciliation_log confidence scores highlight the affected segment |
| PII exposure in Bronze | Bronze restricted to data engineers; 90-day retention; audit logs enabled |
| Schema drift from a source | Strict schema mode quarantines new columns; manual approval gate before Silver changes |
| Monthly cost exceeds budget as volume grows | DBU alarm on the billing dashboard; move Gold to incremental merge before adding compute |

### Future Considerations

- Stripe webhooks feeding a streaming Bronze path for sub-minute payment
  latency (v2).
- A separate feature-engineering layer for churn-scoring models.
- Cross-workspace orchestration and lineage if a second data product shares
  the Silver customer dimension.
- External tables or Delta Sharing if query volume from other warehouses grows.

Reference

ActivityDesign — Decide how to build it. Capture trade-offs, contracts, and architecture decisions.
Default locationdocs/helix/02-design/data-architecture.md
RequiresNone
EnablesNone
InformsData Quality Expectations
Technical Design
Solution Design
Referenced byData Quality Expectations
Implementation Plan
Runbook
Generation prompt
Show the full generation prompt
# Data Architecture Generation Prompt

Document the data pipeline architecture that the team needs to build, review,
operate, and evolve the data product.

## Purpose

Data Architecture is the **highest-authority structural artifact for data pipeline
design** in the Design activity. Its unique job is to describe the durable pipeline
shape: ingestion patterns, medallion layer topology, streaming vs. batch semantics,
transformation patterns, governance boundaries, quality gates, and critical
performance or cost tradeoffs.

Data Architecture is not a data model (captured in Data Design), implementation plan,
or ADR. It is the bridge between PRD (kind: data) (requirements) and implementation: "given
these requirements, here is how the pipeline is structured."

## Reference Anchors

Use these local resource summaries as grounding:

- `docs/resources/databricks-lakehouse-medallion-architecture.md` grounds
  medallion topology (Bronze/Silver/Gold layer responsibilities, transformations,
  and quality gates).
- `docs/resources/databricks-auto-loader.md` grounds cloud-native ingestion
  patterns for incremental, scalable, schema-aware source connectors.
- `docs/resources/databricks-streaming-tables.md` grounds declarative streaming
  and materialized views for real-time transformations and quality enforcement.
- `docs/resources/databricks-sdp.md` grounds SDP lineage, governance, and
  quality-first design through `EXPECT ... ON VIOLATION ...` clauses and
  contract-driven pipeline composition.

## Focus

- Sketch the medallion layer flow: what lands in Bronze, what transformations
  happen in Silver, what business tables live in Gold.
- Name ingestion patterns (Auto Loader, Streaming Tables, batched SQL, CDC) and
  why each is used for its source.
- Document transformation semantics: idempotence, exactly-once vs. at-least-once,
  stateful operations, and how schema evolution is handled.
- Specify governance and quality checkpoints: where data is validated, which
  layers enforce which contracts, and how SLA compliance is monitored.
- Call out critical performance or cost tradeoffs: partitioning strategy,
  clustering, retention policy, incremental refresh vs. full rebuild.

## Role Boundary

Data Architecture describes pipeline topology and data flow, not the detailed
data model (Data Design), not implementation sequences (Implementation Plan),
and not individual quality checks (Data Quality Expectations).

**Non-Databricks platforms:** see
`docs/resources/databricks-platform-substitution.md` for the equivalent
terms on Snowflake, BigQuery, and on-prem stacks. The artifact shape and
prompt stay the same.

## Completion Criteria

- Medallion layer diagram or description is clear (what lands where, why).
- Each layer's transformation responsibilities are explicit.
- Ingestion patterns name actual technologies and explain why each is used.
- Quality gates are named (where validation happens, what contracts are
  enforced).
- Performance/cost tradeoffs are visible (partitioning, clustering, retention,
  refresh strategy).
- Deployment topology is concrete (number of clusters, auto-scaling, failover).
- Major decisions link to PRD (kind: data) requirements or include inline rationale.
Template
Show the template structure
---
ddx:
  id: data-architecture
  authoring:
    home: repo
---

# Data Architecture

Platform- and pipeline-level shape of the data product: medallion topology,
processing-framework choices, governance model, and pipeline-level quality
contracts. Entity-level modelling (logical schema, access patterns,
constraints, migration) lives in [[data-design]].

## Overview

[Describe the data product being architected, the business problem it solves,
and the system context. Name the key data flows and platform fit. Reference
[[prd]] (kind: data) for the requirements and success metrics this architecture must
satisfy.]

### Scope

[What data flows and systems are covered. What is deliberately out of bounds.
Which requirements from [[prd]] (kind: data) drive the design decisions.]

### System Context

| External System | Role | Protocol | Data Volume |
|-----------------|------|----------|------------|
| [Source system] | [Role in the pipeline] | [API, batch export, CDC] | [Order-of-magnitude per period] |
| [Consumer system] | [How it consumes Gold] | [Delta share, SQL, API] | [Query volume] |

```mermaid
graph TB
    A["Source A"] -->|ingest| B["Data Platform"]
    C["Source B"] -->|ingest| B
    B -->|consumption layer| D["BI / Reporting"]
    B -->|feature store| E["ML Platform"]
```

## Medallion Topology

### Layer Strategy

[State the medallion strategy: Bronze (raw), Silver (validated), Gold
(consumption). For each layer, name the transformation scope, quality gates,
and consumer responsibilities. Justify the choice against [[prd]] (kind: data)
freshness and quality requirements.]

### Bronze Layer (Raw Ingestion)

- **Purpose**: Land source data in its native form without transformation.
- **Source integration pattern**: [Auto Loader, Streaming Tables, scheduled
  batch import, CDC]
- **Schema handling**: [Strict / inferred / evolution policy]
- **Retention policy**: [Rationale tied to cost and replay needs]

Responsibilities:
- Ingest all records from source.
- Preserve source schema exactly (no renames or coercion).
- Tag records with ingest timestamp and source-system identifier.
- Quarantine records that fail schema validation.

Quality gates: ingest-metadata presence, no column truncation, source
availability watchdog.

### Silver Layer (Validated and Transformed)

- **Purpose**: Cleansed, deduplicated, business-logic-ready data.
- **Deduplication strategy**: [Key + ordering rule]
- **Type coercion / null policy**: [Defaults vs reject]
- **Referential integrity**: [Which FK relationships are enforced and how]

Join strategy (pipeline-level — entity-level joins live in [[data-design]]):

| Join | Source Layers | Type | Cardinality | Latency Impact |
|------|---------------|------|-------------|----------------|
| [Logical join name] | [Left / Right] | [Inner / Outer] | [1:1 / 1:N] | [Qualitative] |

Quality gates: PK uniqueness, NOT NULL on critical columns, row-count
reconciliation with Bronze within tolerance.

### Gold Layer (Consumption)

- **Purpose**: Business-ready tables optimised for consumer queries.
- **Optimisation strategy**: [Partitioning, clustering / z-order, materialised
  views — at the pipeline level, not column-level]
- **Retention policy**: [Compliance and analytics horizon]

Consumption tables (entity definitions live in [[data-design]]):

| Table | Use Case | Consumers | Freshness Target |
|-------|----------|-----------|------------------|
| [Gold table name] | [Use case from PRD (kind: data)] | [Persona] | [Target tied to SLA] |

Quality gates: aggregate reconciliation with Silver, referential integrity
across Gold, latency within consumer SLA.

## Data Flow

[Describe how data moves through the medallion layers. Clarify ingestion
frequency, transformation latency, and refresh strategy.]

```mermaid
graph LR
    A["Source"] -->|ingest pattern| B["Bronze"]
    B -->|transform job| C["Silver"]
    C -->|aggregate job| D["Gold"]
    D -->|published| E["Consumers"]
```

### Incremental vs Full Refresh

- **Bronze**: [CDC / append / full reload — rationale]
- **Silver**: [Incremental keys / full recalc — rationale]
- **Gold**: [Append-only / snapshot / merge — rationale]

## Processing Semantics

### Streaming vs Batch Decision

| Layer | Strategy | Rationale | SLA Implication |
|-------|----------|-----------|-----------------|
| Bronze | [Streaming / Batch / Incremental] | [Why] | [Freshness achieved] |
| Silver | [Streaming / Batch / Incremental] | [Why] | [Freshness achieved] |
| Gold | [Streaming / Batch / Incremental] | [Why] | [Freshness achieved] |

### Processing Framework

- **Framework**: [Databricks SQL, PySpark, dbt, Streaming Tables, Flink, …]
- **Orchestration**: [Workflows, Airflow, dbt Cloud, Dagster, …]
- **Failure handling**: [Retry policy, dead-letter queue, manual intervention]
- **Idempotence / exactly-once posture**: [Per layer]
- **Schema evolution policy**: [Auto-add / manual approval / strict]

### Latency and Throughput Targets

| Stage | Latency Target | Throughput Target | Binding Constraint |
|-------|----------------|-------------------|--------------------|
| Source → Bronze | [From PRD (kind: data) SLA] | [Order of magnitude] | [Rate limit, API quota] |
| Bronze → Silver | [From PRD (kind: data) SLA] | [Order of magnitude] | [Compute / dedup cost] |
| Silver → Gold | [From PRD (kind: data) SLA] | [Order of magnitude] | [Query complexity] |

## Pipeline-Level Quality Contracts

[Express the contracts the *pipeline* enforces at each layer boundary.
Column-level field rules belong in [[data-quality-expectations]]; this
section names which contracts the architecture commits to enforce and
where.]

### Bronze → Silver

- **Schema contract**: [What Silver requires of Bronze]
- **Volume contract**: [Acceptable row-count delta]
- **Freshness contract**: [Max ingest lag before Silver is held]
- **Violation handling**: [Alert / hold / quarantine]

### Silver → Gold

- **Uniqueness contract**: [Which keys are unique at Gold]
- **Referential contract**: [Which FK relationships are guaranteed]
- **Aggregate-reconciliation contract**: [Sums and counts must agree within
  tolerance]
- **Violation handling**: [Reject / rollback / alert]

### Cross-Layer Contracts

| Contract | Assertion | If Violated |
|----------|-----------|-------------|
| [Row count Bronze → Silver] | [Within tolerance] | [Alert + manual audit] |
| [Cardinality Silver → Gold] | [Stable across refresh] | [Reject until reconciled] |
| [FK integrity across Gold] | [No orphans] | [Quarantine + alert] |

Detailed `EXPECT` clauses, field-level constraints, and freshness predicates
live in [[data-quality-expectations]].

## Governance and Access Control

### Identity and Access Model

| Role | Catalog Scope | Layer Access | Permissions |
|------|---------------|--------------|-------------|
| [Role from PRD (kind: data) consumers] | [Catalog / schema] | [Bronze / Silver / Gold] | [SELECT / MODIFY / EXECUTE] |

### Data Classification and Retention

| Layer | Classification | Sensitive Categories | Retention Policy | Masking Policy |
|-------|----------------|----------------------|------------------|----------------|
| Bronze | [Class] | [Categories — not specific columns; those live in data-design] | [Policy tied to compliance] | [Who sees raw] |
| Silver | [Class] | [Categories] | [Policy] | [Who sees masked vs raw] |
| Gold | [Class] | [Categories] | [Policy] | [Default masking for BI] |

### Fine-Grained Access Control

- **Row-level security**: [Tenant / region predicate — policy, not the
  predicate code, which lives in [[data-design]]]
- **Column-level security**: [Which classifications are masked for which
  roles]
- **Dynamic views**: [Masking-function strategy]

## Platform Design

### Catalog Organisation

```
[catalog]
├── [schema]
│   ├── [bronze table family]
│   ├── [silver table family]
│   └── [gold table family]
├── metadata
│   ├── pipeline_runs
│   └── quality_metrics
```

### Compute Strategy

| Workload | Compute Tier | Sizing Approach | Rationale |
|----------|--------------|-----------------|-----------|
| Bronze ingestion | [Tier] | [Auto-scale bounds / fixed] | [Continuous vs scheduled] |
| Silver transformation | [Tier] | [Sizing approach] | [Batch vs streaming] |
| Gold consumption | [Tier] | [Sizing approach] | [Query pattern] |

Cost-shaping levers (qualitative — concrete numbers belong in operational
runbooks, not the architecture):

- Spot / preemptible instances for retryable workloads.
- Auto-termination of idle clusters.
- Partition pruning and clustering for scan reduction.
- Materialised vs on-demand aggregates.

### Storage Strategy

| Layer | Format | Partitioning | Clustering / Optimisation |
|-------|--------|--------------|---------------------------|
| Bronze | [Delta / Iceberg / …] | [By date / source] | [Compaction policy] |
| Silver | [Format] | [By key / date] | [Z-order / cluster keys] |
| Gold | [Format] | [By query predicate] | [Materialised views / cache] |

### Platform Features in Use

| Feature | Use Case | Configuration Note |
|---------|----------|--------------------|
| [Auto Loader / equivalent] | [Bronze ingestion] | [Trigger mode, schema mode] |
| [Streaming Tables / equivalent] | [Bronze → Silver] | [Trigger / latency target] |
| [Pipeline orchestrator] | [End-to-end refresh] | [Schedule / dependency] |
| [Governance catalog] | [Access + lineage] | [Cross-team sharing posture] |

For non-Databricks platforms, see
[`docs/resources/databricks-platform-substitution.md`](../../../../../docs/resources/databricks-platform-substitution.md)
for the platform-equivalent terms.

## Decisions and Tradeoffs

### Key Architecture Decisions

| Decision | Choice | Rationale | Alternative Considered | Consequence |
|----------|--------|-----------|------------------------|-------------|
| [Medallion layering] | [Choice] | [Why] | [Alternative] | [Tradeoff] |
| [Streaming vs batch per layer] | [Choice] | [Why] | [Alternative] | [Tradeoff] |
| [Compute tier per workload] | [Choice] | [Why] | [Alternative] | [Tradeoff] |

### Performance vs Cost Tradeoffs

- [Real-time vs near-real-time ingestion — freshness gain vs sustained
  compute cost]
- [Materialised vs on-demand Gold aggregates — query latency vs storage]
- [Spot vs on-demand compute — cost savings vs interruption risk]

### Known Risks and Mitigations

| Risk | Mitigation |
|------|------------|
| [Source rate limit causes backlog] | [Backoff + queue buffering + lag alert] |
| [PII exposure in Bronze] | [Masked views + audit logs] |
| [Schema drift from source] | [Schema registry + manual approval gate] |

---

## Review Checklist

- [ ] **Scope** clearly states which data flows are in / out of bounds.
- [ ] **Medallion topology** names Bronze / Silver / Gold purposes and
  transformation rules.
- [ ] **Data flow diagrams** show how data moves through layers and to
  consumers.
- [ ] **Processing semantics** explicitly state streaming vs batch per layer
  with latency targets tied to [[prd]] (kind: data).
- [ ] **Pipeline-level quality contracts** name which contracts each layer
  boundary enforces; detailed `EXPECT` clauses are deferred to
  [[data-quality-expectations]].
- [ ] **Failure handling** specifies what happens when a contract fails
  (alert, reject, quarantine, rollback).
- [ ] **Access control** model covers identity, row-level, column-level,
  and sensitive-data masking at the policy level.
- [ ] **Platform design** names catalog organisation, compute tiering, and
  storage strategy without committing to hardcoded cost numbers.
- [ ] **Decisions and tradeoffs** document key choices with rationale and
  alternatives considered.
- [ ] **Cross-layer contracts** are defined (reconciliation, cardinality,
  no orphans).
- [ ] **SLA per layer** is documented (freshness, latency, availability)
  and traces to [[prd]] (kind: data).
- [ ] No `[TBD]`, `[TODO]`, or `[NEEDS CLARIFICATION]` markers remain.
- [ ] Entity-level details (logical schema, indexes, migrations, store
  selection) are deferred to [[data-design]].
- [ ] For non-Databricks platforms, terms map via
  `docs/resources/databricks-platform-substitution.md`.
Innsigle seal: model-primary by HELIX

The signature covers the markdown source of this page, not these HTML bytes. This page quotes that seal; verify it against the source file.

Composition
model-primary
Issuer
HELIX helix
Signing key
ed25519:b0865d76d834a52c48506414d16f4e5a (build key)
Signed source
artifact-types/design/data-architecture.md
Signed
2026-09-23T14:11:58Z
Content digest
sha256:c55af911…7585d035

This build key is endorsed by the human key for build signing; the signature is not a detector and not a truth guarantee.

Raw attestation JSON
{
  "payload": {
    "innsigle": "1",
    "type": "https://innsigle.dev/claim/colophon/v1",
    "issued_at": "2026-09-23T14:11:58Z",
    "issuer": {
      "id": "helix",
      "name": "HELIX",
      "key_id": "ed25519:b0865d76d834a52c48506414d16f4e5a",
      "key_url": "https://documentdrivendx.github.io/helix/.well-known/innsigle/keys.json"
    },
    "subjects": [
      {
        "uri": "https://documentdrivendx.github.io/helix/artifact-types/design/data-architecture/",
        "digest": {
          "alg": "sha256",
          "value": "c55af911c801ae9a8f2b1f6ac903b90d260a8a3cd6e17d75fc7496e07585d035"
        }
      }
    ],
    "colophon": {
      "schema_version": "1",
      "composition": "model-primary",
      "ingredients": [
        {
          "kind": "model",
          "name": "Claude",
          "role": "draft"
        },
        {
          "kind": "tool",
          "name": "sloptimizer",
          "role": "rewrite"
        },
        {
          "kind": "human",
          "name": "operator",
          "role": "structure-edit"
        }
      ],
      "notes": null
    }
  },
  "payload_encoding": "json",
  "signatures": [
    {
      "key_id": "ed25519:b0865d76d834a52c48506414d16f4e5a",
      "alg": "ed25519",
      "sig": "np1I9MTNkf_YHKJQDfTZKxPa_GMxl0yK55Disu5I9Ld20TahrewXLBLHnYA8nMglN11ZJehSNIc0_KwZz5wIDw",
      "signed_at": "2026-09-23T14:11:58Z"
    }
  ]
}