Architecture One-Pager
Solution Architecture v1.0 · Databricks Lakehouse on AWS · Data Platform Architecture · 2026-09 · 20 views · 16 architecture decision records
Data Quality Service · Solution Architecture v1.0 · Databricks Lakehouse on AWS · Data Platform Architecture · 2026-09 · 20 views · 16 architecture decision records
A verdict binds to an immutable data version, never to the pipeline run that produced it — and the plane that judges data is separate from the plane that circulates it.
Every consumer product people use daily runs a lakehouse behind it, and every one of those lakehouses has the same recurring outage that never gets a status page: a table that is present, fresh-looking and wrong. The job succeeded. The dashboard rendered. The pricing model scored. Nobody was paged, because nothing failed. Four weeks later a merchant disputes an invoice and someone discovers that a currency column silently switched units, or that a restaurant_is_open flag has been true for a chain that shut in March. At that point the useful question is not 'how do we fix it' — the fix is an hour's work upstream — but 'how much of the last four weeks do we have to disbelieve, and who else read it'. Almost no data quality tooling can answer that, because almost all of it records that last night's run passed rather than that a specific version of a specific table was judged and found sound.
Assertions are versioned, owned, priced and compiled to bounded query plans, then pushed down into the lakehouse engine so no dataset ever leaves its storage layer to be judged. Evaluation is triggered by a table-version commit, by a schedule, and by a reconciliation sweep that exists because the event stream will lose something eventually. Every evaluation writes an immutable verdict keyed by the data version and the assertion version that produced it — never by the run — and a derived state index answers the platform's one hot question, 'is this dataset safe to read right now', in under twenty milliseconds with an explicit staleness bound. Enforcement is a separate step that reads that state: an offending row-level batch is quarantined under the source's own classification, a dataset-level failure withholds promotion of the new version so consumers keep reading the last known good one, and nothing anywhere writes a corrected value back into a dataset. Because verdicts are bound to versions, a rule written today can be evaluated against any past version, which is what turns the four-week question into a corruption window, a blast-radius list, and a set of suspect marks somebody has to clear.
What it is, and what it is not
- A verdict keyed by the data version and the assertion version — not a record that last night's pipeline run passed, which cannot be re-derived, cannot be re-judged by a rule written later, and gives a forensic investigation as the only answer to 'how far back does this go'.
- Blocking implemented as non-promotion of a written version — not deleting, rolling back or mutating the data, which destroys the evidence and makes the quality service a writer to the estate it is meant to judge.
- 'Unknown' and 'no coverage' rendered as their own states — not a green tick that means either 'forty assertions passed' or 'nobody ever wrote one', which is the defect that lets a silently corrupt table look healthy for a month.
- Quarantine restricted to row-attributable violations, with a ceiling that escalates to block — not quarantine as a general-purpose safety valve, where diverting a quarter of a batch disguises a broken upstream as partial success.
- A cost estimate posted before an assertion is published, and a per-dataset budget at run time — not unbounded validation queries discovered in next month's bill, which is how quality coverage gets cut as a cost measure.
- Automatic demotion of an assertion above a measured false-positive rate — not a growing set of alerts everyone has learned to ignore, which is the normal end state of a data quality programme.
- Overrides and suppressions as expiring objects that create a debt item — not configuration flags, where the exception outlives the incident and nobody can list what is currently unguarded.
- Evaluation pushed down into the engine the pipelines already use — not a separate validation runtime that copies data out to check it, duplicating cost, storage governance and the credential path into every dataset.
The decisions that are the architecture
- Verdicts bind to data versions, not to runs (ADR-01) — The single decision the whole architecture rests on. It makes history re-judgeable, lets a rule written in September condemn a window in July, and turns 'how far back does this go' into a query.
- The service judges; it never repairs (ADR-02) — No component writes a corrected value into a dataset. Blocking is non-promotion, so the rejected version stays inspectable and a service outage degrades to 'consumers read the last known good'.
- The gate is a separate step that reads state (ADR-03) — The quality service publishes a claim about a version; promotion is a distinct act that consults it. That keeps the service off every write path without making enforcement a matter of goodwill.
- Current state is a derived projection with a published staleness bound (ADR-04) — Four thousand reads a second against a far lower verdict rate. The index is rebuildable from the verdict store, which is why its recovery objective is a rebuild rather than a restore.
- Absence of a verdict is never a pass (ADR-05) — 'Unknown' and 'no coverage' are first-class states, and a Tier 1 dataset in either is not promoted. Fail-safe, not fail-open.
- Computation goes to the data, on declared priority lanes (ADR-07) — Assertions compile to engine-native plans and fuse into one pass per partition. Backfill runs in its own lane so re-judging history never delays judging the present.
- Retroactive evaluation with a mandatory human judgement (ADR-10) — A new rule is backfilled over a 30-day lookback and the result reported before it may enforce, because a rule that condemns 18 of the last 30 versions is as likely to be a bad rule as a discovery.
- A bad rule cannot take down a pipeline (ADR-12) — Per-assertion circuit breaking, probation for new rules, and automatic demotion above a 5% false-positive rate — structural, not operational courtesies.
- Every escape hatch expires and creates debt (ADR-16) — Overrides and suppressions are objects with an owner, a justification and an expiry, written to an immutable log before the state changes. Unbounded suppression is unsupported.
Why this should still be right in ten years
A data quality service outlives the warehouse it was built against, because the judgements it recorded are what a future team uses to decide whether to trust a number from today. These are the properties that should survive a change of engine, of cloud, and of the people who built it.
- The version binding depends on no technology. ADR-01 needs one thing from the storage layer: an immutable, addressable version of a dataset. Delta gives it, Iceberg gives it, Hudi gives it, and a partitioned object layout with a manifest gives it. What would break the decision is not a migration but a future team recording verdicts against a job id because it was easier to join — which is why the version key is drawn in the data model rather than left to an implementation note.
- 'Unknown is not pass' is the claim that ages best. Every threshold in this design will be renegotiated: the 2% ceiling, the 5% false-positive rate, the 15-minute Tier 1 SLA, the 2.5% cost cap. None of them is load-bearing in the way ADR-05 is. A platform that renders 'nobody checked' and 'forty checks passed' identically will produce the silent-corruption case again no matter how good its rules are, and that is true of any tooling in any decade.
- The judging/circulating separation is what keeps the service safe to depend on. Because the quality service only publishes claims, its own failure has a defined, benign meaning: consumers read the last known good version. The moment a future team puts promotion inside the quality service to make enforcement airtight — a reasonable-sounding change — a quality service outage becomes a platform-wide write outage. ADR-03 states the boundary explicitly so that trade is made deliberately if it is ever made.
- Expiring exceptions are the only mechanism that survives organisational pressure. Quality debt does not accumulate because people are careless; it accumulates because an exception granted during an incident has no natural end. ADR-16 makes the end structural. Of everything here, this is the decision most likely to be quietly weakened — by a 'permanent exemption' field added for a good reason — and the one whose loss would be hardest to detect.
- The false-positive rate is what decides whether any of this is enforced. The uncomfortable truth about data quality platforms is that they fail socially, not technically: the alerts become noise and the gate becomes advisory in practice while remaining mandatory on paper. Measuring the false-positive rate per assertion and acting on it automatically is the only defence that does not depend on someone's attention, and it will still be the answer when the assertions are generated by a model rather than typed by an engineer.
Non-functional targets
Every number here is a stated assumption from the requirement, chosen to be argued with. The right-hand column names the view where the mechanism that meets it is drawn.
| Quality | Target | How it is met | View |
|---|---|---|---|
| State read availability | ≥ 99.95% monthly | Derived projection in a DynamoDB global table, read path replicated to a second region, and readable independently of the control plane so a control-plane outage does not stop consumers getting an answer. | 16 |
| Control plane availability | ≥ 99.5% monthly | Stateless APIs on EKS across three AZs with a leader-elected scheduler; Aurora multi-AZ behind it. A control-plane outage delays new verdicts, it does not change existing state. | 07 |
| Evaluation plane availability | ≥ 99.0% monthly | Deliberately the weakest target of the three: a missed evaluation window is recoverable by the reconciliation sweep, a wrong verdict is not. | 12 |
| Verdict publication latency | ≤ 90 s p95, ≤ 180 s p99 after a Tier 1 commit | Commit-event trigger, metadata assertions evaluated first without a scan, and all scan assertions for a partition fused into a single pass. | 11 |
| State read latency | ≤ 20 ms p99, ≤ 8 ms p50 in-region | Key-value projection keyed by dataset and partition, sized for the orchestrator's pre-run checks rather than for human traffic. | 08 |
| Freshness detection | ≤ 6 min for Tier 1 (1 cadence + 60 s) | Five-minute evaluation cadence against a 15-minute staleness SLA, with an unevaluated freshness assertion counted as breached. | 13 |
| Throughput | 180,000 evaluations/day; 9,000/hour peak; 4× burst for 90 min | Per-dataset work partitioning with per-domain concurrency quotas, coalescing by (dataset, partition) rather than per commit so a backfill storm does not multiply work. | 14 |
| State read throughput | 4,000 reads/s sustained, 12,000/s peak | Read path scaled independently of evaluation, since it is exercised by every consumer job and orchestrator task at roughly 3,000 times the verdict write rate. | 16 |
| Scale | 12,000 datasets, 40,000 active assertions, 2.4 PB | Assertions fused per dataset scan and compiled to bounded plans, so cost grows with data scanned rather than with assertion count. | 06 |
| Verdict retention | 25 months, hot for 90 days | Append-only Delta on S3 with lifecycle tiering; sized to two annual audit cycles plus margin. | 09 |
| Override audit retention | 7 years, immutable | S3 Object Lock in compliance mode, written before the state change it authorises, so the record cannot be the thing that failed. | 20 |
| Recovery | Control plane RPO 5 min / RTO 30 min; verdict store RPO 1 min / RTO 60 min; state index RPO 15 min / RTO 10 min | The state index has the tightest RTO and the loosest RPO because it is fully re-derivable: its recovery is a rebuild from verdicts, not a restore. | 09 |
| Detection quality | MTTD ≤ 4 h p90 (Tier 1 correctness), ≤ 24 h p90 (silent-corruption class) | Measured per assertion type as a first-class platform metric, because MTTD rather than alert count is the measure of whether the service works. | 18 |
| Alert credibility | ≤ 5% false-positive rate per assertion over 30 days | Tracked per assertion with automatic demotion to advise above the threshold. The single most consequential number in the design: above it, the gate is decorative. | 15 |
| Coverage | 100% of Tier 1 datasets carry schema + freshness + one aggregate assertion | Coverage rendered as part of the verdict, and zero-coverage datasets surfaced on the domain scorecard as debt. | 18 |
| Cost | ≤ 2.5% of data platform compute spend | Per-dataset budgets, authoring-time cost estimation, metadata-only evaluation preferred over scans, and the full-scan fraction reported per dataset. | 17 |
Scope
In scope
- Versioned assertion authoring in a rule DSL held in version control, with tier and verdict action declared at authoring time.
- Push-down evaluation triggered by table-version commit, by schedule, and by a reconciliation sweep, with assertions fused per dataset scan.
- An immutable verdict store keyed by data version and assertion version, and a derived current-state index with a published staleness bound.
- Freshness SLAs per dataset tier, distinguishing arrival staleness from event-time staleness.
- Three verdict actions — advise, quarantine, block — with blocking implemented as non-promotion and quarantine restricted to row-attributable violations.
- Retroactive evaluation, automatic backfill of new assertions, corruption-window computation and one-hop suspect propagation.
- Attributed, expiring overrides and suppressions, an immutable audit log, and a quality debt register that ages visibly.
- Per-dataset and per-domain scorecards, cost attribution, and the detection-quality metrics that say whether the practice is working.
Explicitly out of scope
- Scheduling or running the pipelines that produce the data — the service consumes their commits and never orchestrates them.
- Repairing data. No component writes a corrected value; repair is upstream and is then re-judged.
- Owning or arbitrating data contracts with producers. Contracts are imported as a source of assertions, not negotiated here.
- Master data management, entity resolution, and record-level reconciliation between systems of record.
- Full transitive suspect propagation and column-level lineage — deferred to Phase 3 pending lineage precision (ADR-11).
- Continuously-written and streaming table evaluation at checkpoint granularity — deferred to Phase 3, and the open question the MVP does not answer.
- Automated re-derivation of downstream datasets once a corruption window closes.
What a four-week prototype should prove
Three of this architecture's claims are cheap to test and expensive to be wrong about. A prototype that proves these can be built on; one that skips them has demonstrated running a SQL query against a table, which is the easy half.
- Retroactive equivalence: take a rule, evaluate it live against ten consecutive versions of a real Tier 1 table, then evaluate the same rule version against the same ten versions as a backfill a week later, and show the verdicts are byte-identical. If time travel and the live path can disagree, ADR-01 is a slogan rather than a mechanism.
- Fusion under realistic assertion counts: forty assertions of mixed type on one partitioned fact table, and the achieved fusion ratio and wall-clock cost measured against evaluating them one at a time. The cost model in ADR-08 assumes the ratio is high; if it is not, the 2.5% ceiling forces a coverage decision nobody has made.
- The gate under a control-plane outage: kill the control plane and show that the orchestrator's pre-run check still answers from the state index, with a staleness value the consumer can act on, and that a Tier 1 dataset in unknown state is not promoted. ADR-03, ADR-04 and ADR-05 are one claim from the consumer's side, and this is the test of it.
- Drop a commit event for a Tier 1 table and confirm the reconciliation sweeper enqueues the unjudged version within one cycle, that the version reads 'unknown' rather than 'good' in the meantime, and that it is not promoted.
- Kill the control plane mid-evaluation and confirm the orchestrator's pre-run check still answers from the state index, with a staleness value attached, and that a consumer past the published bound degrades to unknown rather than reusing its last answer.
- Publish an assertion whose plan the compiler cannot bound and confirm it is refused at merge rather than discovered at run time; then publish one that exceeds its dataset budget at run time and confirm the verdict is 'degraded' with the metadata assertions still evaluated.
- Exhaust an assertion's execution repeatedly — a deliberately poisonous predicate — and confirm the circuit breaks for that assertion alone while its thirty-nine siblings on the same table continue to produce verdicts.
- Grant an override on a blocked version, let it expire, and confirm the state reverts without anyone acting, the debt item ages rather than closing, and the audit entry was written before the state changed.
Open risks, carried rather than hidden
| Risk | If it lands | Response |
|---|---|---|
| The gate is advisory in practice | Consumers and pipelines read state until the first incident where the gate is inconvenient, then stop. The platform continues reporting verdicts nobody acts on, and the silent-corruption case returns with a dashboard attached. | Instrument the gate read itself: 'no gate reads from a consumer that used to read' is an alert in view 18. Enforcement that cannot be observed is not enforcement. |
| Distributional baselines learn the corruption | A slow drift — a unit change rolled out gradually, a growing share of a new merchant category — is absorbed into the 28-day baseline and never fires. The assertion type intended to catch silent corruption becomes the one that ratifies it. | Keep reconciliation assertions against independently computed control totals as the backstop for anything financial, and treat a baseline retrained during an open corruption window as invalid. |
| Suspect marks accumulate and stop meaning anything | One-hop propagation from a handful of upstream defects leaves hundreds of tables marked, nobody clears them, and consumers learn to ignore the mark — the same failure as alert fatigue, one layer down. | Marks are debt items with owners and ages, surfaced on domain scorecards. If the median age climbs, the propagation depth is wrong and ADR-11 gets revisited. |
| Quality work is starved by the compute it shares | Tier 1 verdicts arrive late precisely during a platform incident, because the warehouses are saturated by the pipelines whose output most needs judging. | Reserved capacity for the Tier 1 live lane with backfill on spot; measure verdict lag during the next real incident rather than in steady state, and revisit ADR-07 if the lane cannot be held. |
| Coverage is thin where it matters and thick where it is easy | Twelve thousand datasets with forty thousand assertions can still leave the currency column of the payments fact table unguarded, which is exactly the shape of the original defect. | Coverage measured against each dataset's declared contract rather than as an absolute count, with zero-coverage Tier 1 datasets blocked from being declared consumable. |
| Lineage precision bounds the blast-radius claim | The corruption-window report reads as complete but omits any downstream consumer the catalogue does not know about — a notebook, an export, a model trained from a snapshot. | State the lineage coverage percentage on the report itself, so a blast-radius list is read as a floor rather than as an inventory. |
The reasoning behind every component and technology choice is in the Architecture Decision Record: 16 records across 6 areas, each with the alternatives that lost and what the choice costs.