Skip to content

Validation Engine

Status: Final Version: 1.0


Purpose

Define the comprehensive validation pipeline that executes schema, domain, referential, evidence, business rule, and anomaly checks against submissions.


Validation Types (Execution Order)

Order Type Failure Impact Examples
1 Schema Hard fail (REJECTED) Data type, required fields, format
2 Domain Hard fail (REJECTED) Min/max, regex, precision
3 Referential Hard fail (REJECTED) Cross-field consistency, sum checks
4 Evidence Hard fail (REJECTED) Required evidence attached
5 Business Hard fail (REJECTED) Custom logic, conditional rules
6 Anomaly Warning only Statistical outliers, YoY spikes

Laravel Validation Service

class ValidationService
{
    public function validate(MetricSubmission $submission): ValidationResult
    {
        $metric = $submission->metricDefinition;
        $rules = $metric->validation_rules;
        $errors = [];
        $warnings = [];

        // 1. Schema Validation
        foreach ($rules->where('type', 'schema') as $rule) {
            if (!$this->validateSchema($submission, $rule)) {
                $errors[] = $rule->error_message;
            }
        }

        // Stop if schema fails
        if (!empty($errors)) {
            return new ValidationResult(false, $errors, $warnings);
        }

        // 2-5. Other validations...
        // 6. Anomaly Detection (warnings only)
        foreach ($rules->where('type', 'anomaly') as $rule) {
            if (!$this->checkAnomaly($submission, $rule)) {
                $warnings[] = $rule->error_message;
            }
        }

        return new ValidationResult(empty($errors), $errors, $warnings);
    }

    protected function validateSchema(MetricSubmission $submission, $rule)
    {
        $value = $submission->raw_data['value'];

        return match($rule->rule) {
            'required' => !is_null($value) && $value !== '',
            'numeric' => is_numeric($value),
            'integer' => is_int($value) || (is_numeric($value) && floor($value) == $value),
            'boolean' => is_bool($value),
            'date' => $this->isValidDate($value),
            default => true,
        };
    }

    protected function checkAnomaly(MetricSubmission $submission, $rule)
    {
        if ($rule->rule === 'z_score') {
            $historicalValues = $this->getHistoricalValues($submission);
            $zScore = $this->calculateZScore($submission->raw_data['value'], $historicalValues);
            return abs($zScore) <= $rule->threshold;
        }

        if ($rule->rule === 'yoy_change') {
            $lastYearValue = $this->getLastYearValue($submission);
            if (!$lastYearValue) return true; // No baseline, skip check

            $change = abs(($submission->raw_data['value'] - $lastYearValue) / $lastYearValue * 100);
            return $change <= $rule->max_percentage;
        }

        return true;
    }
}

Validation Result Storage

CREATE TABLE validation_results (
    id BIGSERIAL PRIMARY KEY,
    metric_submission_id BIGINT NOT NULL REFERENCES metric_submissions(id),
    passed BOOLEAN NOT NULL,
    errors JSONB, -- Array of error messages
    warnings JSONB, -- Array of warning messages
    validated_at TIMESTAMP DEFAULT NOW(),
    INDEX (metric_submission_id)
);

Acceptance Criteria

  • All 6 validation types implemented
  • Schema validation stops pipeline if failed
  • Anomaly checks produce warnings only (not hard failures)
  • Validation results stored in dedicated table
  • Collector receives field-level error details via API

Cross-References


Change Log

Version Date Author Changes
1.0 2026-01-03 Senior Product Architect Initial validation engine specification