DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Related

  • From Chaos to Clarity: Building a Data Quality Framework That Actually Works
  • Why I Built the Ultimate Text Comparison Tool (And Why You Should Try It)
  • Why Text2SQL Alone Isn’t Enough: Embracing TAG
  • Instant Integrations With API and Logic Automation

Trending

  • Open Source as a Leadership Lab for Software Engineers
  • Prompt, Fine-Tune, or Compile: The Three Ways to Build Anything in AI
  • Containerizing Spark and Lakehouse Development with Docker
  • Containerizing LLMs: Best Practices for Docker-Based AI Workloads
  1. DZone
  2. Data Engineering
  3. Data
  4. Stop Hardcoding Database Checks: Building a Metadata-Driven Data Quality Framework

Stop Hardcoding Database Checks: Building a Metadata-Driven Data Quality Framework

Decouple validation from code. Learn how to build a dynamic, metadata-driven data quality framework using Databricks, Snowflake, and Python.

By 
Kshitish Nath user avatar
Kshitish Nath
·
Sep. 01, 26 · Analysis
Likes (0)
Comment
Save
Tweet
Share
6 Views

Join the DZone community and get the full member experience.

Join For Free

In high-volume data platforms, hardcoding validation logic into individual processing pipelines creates significant operational drag. As an enterprise data asset footprint grows, maintaining manual checks for hundreds of tables inevitably leads to mounting technical debt, silent schema drift, and a fragmented audit trail.

To achieve data governance at scale, data architects must decouple validation rules from the execution engine. By utilizing a centralized metadata repository to dynamically generate validation suites, organizations can transform data quality from a reactive, script-based bottleneck into a configuration-driven infrastructure asset.

The Metadata-Driven Architecture

Instead of embedding validation constraints directly inside an ETL/ELT pipeline, this pattern isolates validation rules inside a centralized relational database schema. The orchestration engine programmatically queries this metadata at runtime, constructs the validation suites on the fly, executes them against target tables, and routes the evaluation metrics to an observability layer.

This architecture provides three primary engineering advantages:

  • Decoupled Governance: Data stewards can alter business rules or add expectations via simple DML updates without modifying or redeploying production application code.
  • Schema Drift Resilience: The engine dynamically adapts to structural variations by programmatically evaluating target datasets against rules defined at the column level.
  • Centralized Observability: Every rule execution generates a standardized, traceable metric payload, laying a consistent foundation for real-time data auditing and data lineage maps.

1. Defining the Metadata Schema (DDL)

To implement this framework in an enterprise Lakehouse ecosystem, the metadata table must act as an immutable source of truth for constraints. Below is the production DDL required to initialize the control directory in Snowflake or Databricks:

SQL
 
CREATE TABLE data_quality_rules (
    rule_id INT IDENTITY(1,1),
    table_name VARCHAR(255) NOT NULL,
    column_name VARCHAR(255) NOT NULL,
    expectation_type VARCHAR(255) NOT NULL,
    expectation_kwargs VARIANT NOT NULL, -- Stored as JSON object
    is_active BOOLEAN DEFAULT TRUE,
    updated_at TIMESTAMP_NTZ DEFAULT CURRENT_TIMESTAMP(),
    CONSTRAINT pk_rule_id PRIMARY KEY (rule_id)
);

-- Seed metadata rules for execution tracking
INSERT INTO data_quality_rules (table_name, column_name, expectation_type, expectation_kwargs)
VALUES 
('CUSTOMERS', 'CUST_ID', 'expect_column_values_to_not_be_null', '{}'),
('CUSTOMERS', 'AGE', 'expect_column_values_to_be_between', '{"min_value": 18, "max_value": 60}'),
('ORDERS', 'ORDER_ID', 'expect_column_values_to_not_be_null', '{}');


2. Implementation: The Programmatic Execution Engine

The core execution wrapper leverages Python and Great Expectations (gx) to programmatically turn rows of metadata into active validation suites. This script establishes a secure database connection via SQLAlchemy, harvests active constraints, generates runtime batch requests, and triggers structured checkpoints.

Python
 
import os
import json
import logging
from datetime import datetime
import pandas as pd
from sqlalchemy import create_engine
import great_expectations as gx
from great_expectations.core.batch import RuntimeBatchRequest

# Configure structured logging for production auditing
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)

class MetadataDataQualityEngine:
    def __init__(self, connection_string: str):
        self.engine = create_engine(connection_string)
        # Initialize Great Expectations ephemeral context for programmatic runtime control
        self.context = gx.get_context(context_root_dir=None)

    def fetch_active_metadata(self) -> pd.DataFrame:
        """Harvests active validation configurations from the centralized store."""
        query = """
            SELECT table_name, column_name, expectation_type, expectation_kwargs 
            FROM data_quality_rules 
            WHERE is_active = TRUE
        """
        try:
            df = pd.read_sql(query, self.engine)
            logger.info(f"Successfully harvested {len(df)} active validation rules.")
            return df
        except Exception as e:
            logger.error(f"Failed to query metadata repository: {str(e)}")
            raise

    def compile_expectation_suite(self, table_name: str, rules_df: pd.DataFrame):
        """Assembles validation rules into a Great Expectations suite on the fly."""
        suite_name = f"{table_name}_suite"
        suite = self.context.add_or_update_expectation_suite(expectation_suite_name=suite_name)
        
        # Filter metadata constraints for the specific target asset
        table_rules = rules_df[rules_df['table_name'] == table_name]
        
        for _, row in table_rules.iterrows():
            # Parse JSON kwargs configuration gracefully
            kwargs = row['expectation_kwargs']
            if isinstance(kwargs, str):
                kwargs = json.loads(kwargs)
                
            kwargs['column'] = row['column_name']
            
            # Programmatically map string values to structured GX expectation objects
            expectation_config = gx.core.ExpectationConfiguration(
                expectation_type=row['expectation_type'],
                kwargs=kwargs,
                meta={"notes": f"Automated constraint enforcement for column: {row['column_name']}"}
            )
            suite.add_expectation(expectation_config)
            
        self.context.add_or_update_expectation_suite(suite=suite)
        return suite

    def execute_quality_checkpoint(self, table_name: str, target_df: pd.DataFrame):
        """Builds a runtime batch request and evaluates data against the generated suite."""
        timestamp = datetime.utcnow().strftime("%Y%m%dT%H%M%SZ")
        suite_name = f"{table_name}_suite"
        checkpoint_name = f"{table_name}_checkpoint"

        # Unique runtime composite signature prevents processing trace collisions
        batch_request = RuntimeBatchRequest(
            datasource_name="lakehouse_runtime_datasource",
            data_connector_name="runtime_data_connector",
            data_asset_name=f"{table_name}_{timestamp}",
            runtime_parameters={"batch_data": target_df},
            batch_identifiers={"table_name": table_name, "execution_timestamp": timestamp}
        )

        # Register and fire a dynamic checkpoint execution
        self.context.add_or_update_checkpoint(
            name=checkpoint_name,
            config_version=1,
            class_name="SimpleCheckpoint",
            validations=[{
                "batch_request": batch_request, 
                "expectation_suite_name": suite_name
            }]
        )

        logger.info(f"Launching data quality checkpoint for table: {table_name}")
        return self.context.run_checkpoint(checkpoint_name=checkpoint_name)

# Production Loop Execution Pattern
if __name__ == "__main__":
    SF_CONN = "snowflake://<user>:<pass>@<account>/<db>/<schema>?warehouse=COMPUTE_WH&role=SYSADMIN"
    
    dq_engine = MetadataDataQualityEngine(connection_string=SF_CONN)
    metadata_rules = dq_engine.fetch_active_metadata()
    distinct_target_tables = metadata_rules['table_name'].unique()

    for current_table in distinct_target_tables:
        try:
            # Stage current batch dataset from target engine
            raw_data_df = pd.read_sql(f"SELECT * FROM {current_table}", dq_engine.engine)
            
            # Step 1: Build suite dynamically from relational rules
            dq_engine.compile_expectation_suite(table_name=current_table, rules_df=metadata_rules)
            
            # Step 2: Validate batch data and extract metrics payload
            eval_result = dq_engine.execute_quality_checkpoint(table_name=current_table, target_df=raw_data_df)
            
            if not eval_result["success"]:
                logger.warning(f"Data Quality anomalies detected on asset: {current_table}")
            else:
                logger.info(f"Asset {current_table} successfully cleared all metadata expectations.")
                
        except Exception as err:
            # Fault isolation ensures an asset failure never crashes cascading pipeline steps
            logger.error(f"Processing loop interrupted on asset {current_table}: {str(err)}")
            continue


3. Production-Grade Engineering Guardrails

Building a dynamic system requires putting structural guardrails around the execution engine to prevent it from failing under enterprise pressures.

  • Fault Isolation and Pipeline Resilience: Never let a validation failure on an upstream or non-critical business table halt your entire orchestration loop. Wrapping individual target assets in localized try-except blocks ensures that a failure on a secondary table (like CUSTOMERS) does not block downstream transactional tables (like ORDERS) from completing their validation lifecycles.
  • Idempotency and Batching Identifiers: Every unique quality run must be traceable back to a specific moment in time to avoid overwriting or colliding results in your metadata tracking layer. Pair the table_name with an immutable execution_timestamp (such as a UTC ISO string) as a composite batch identifier. This guarantees an explicit audit trail across parallel streaming windows or backfilled data runs.
  • Metadata-as-Code Frameworks: Treat the validation matrix table with the same operational rigor as production application code. Changes, additions, or deprecations of quality thresholds must follow a strict GitOps progression. Use schema migration version control tools (like Flyway or Liquibase) to manage, track, and deploy DML changes safely across staging and production clusters.
  • Proactive Alerting Integration: Local HTML docs are insufficient for zero-downtime platforms. The metadata evaluation output dictionary must be integrated directly into cloud native alerting systems. Configure Webhook integrations or cloud alerting channels to route failed validation metrics directly to Slack channels or PagerDuty schedules. This ensures on-call engineers are proactively notified the moment a metric payload trends outside acceptable operational thresholds.

Summary: The Architectural Impact

Transitioning to a metadata-driven approach shifts data quality from a reactive "clean-up" task to an integrated, proactive engineering asset. By treating validation criteria as configurable metadata parameters rather than hardcoded script directives, architects eliminate technical debt and bridge the gap between business semantics and computing layers.

This decoupled architecture provides the strict governance framework required to support high-stakes analytics and downstream machine learning layers, ensuring that every data element hitting your warehouse is automatically and transparently vetted before reaching production consumers.

Data quality Database Framework

Opinions expressed by DZone contributors are their own.

Related

  • From Chaos to Clarity: Building a Data Quality Framework That Actually Works
  • Why I Built the Ultimate Text Comparison Tool (And Why You Should Try It)
  • Why Text2SQL Alone Isn’t Enough: Embracing TAG
  • Instant Integrations With API and Logic Automation

Partner Resources

×

Comments

The likes didn't load as expected. Please refresh the page and try again.

  • RSS
  • X
  • Facebook

ABOUT US

  • About DZone
  • Support and feedback
  • Community research

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 215
  • Nashville, TN 37211
  • [email protected]

Let's be friends:

  • RSS
  • X
  • Facebook