Volume 3: Human-System Collaboration

Pattern 14: Cross-Field Validation

Part II: Interaction Patterns - Relationship Patterns


Opening Scenario: The Date That Came Before It Started

Michael was booking a conference room. He filled out the reservation form:

Start Date: 2024-12-15
End Date: 2024-12-10

He clicked Submit. The system accepted it.

The facilities manager, Sarah, looked at the reservation in disbelief. "The event ends five days before it starts? That's impossible!"

She looked at the validation code:

// Bad: Only validates individual fields
function validateStartDate(date) {
  return date instanceof Date && !isNaN(date);
}

function validateEndDate(date) {
  return date instanceof Date && !isNaN(date);
}

// Both dates are valid dates!
// But the relationship between them is invalid

Sarah added cross-field validation:

// Better: Validate the relationship
function validateDateRange(startDate, endDate) {
  if (endDate <= startDate) {
    return {
      valid: false,
      message: "End date must be after start date",
      fields: ['startDate', 'endDate']
    };
  }

  return { valid: true };
}

But Sarah discovered many more cross-field validation issues:

Budget allocation:

Category A: $5,000
Category B: $3,000
Category C: $4,000
Total: $12,000

Budget Limit: $10,000  ← Problem!

Each category was valid individually, but together they exceeded the limit.

Contact information:

Primary Contact: John Smith
Email: john@example.com
Phone: (555) 123-4567

Emergency Contact: John Smith  ← Same person!
Email: john@example.com
Phone: (555) 123-4567

All fields individually valid, but logically wrong - emergency contact can't be the same person.

Shipping calculations:

Item 1: $50.00
Item 2: $30.00
Item 3: $20.00

Subtotal: $95.00  ← Should be $100.00

Line items were valid, but subtotal didn't match their sum.

Password confirmation:

Password: MySecurePass123!
Confirm Password: MySecurePass124!  ← Doesn't match

Both passwords met complexity requirements individually, but they didn't match each other.


Sarah built a comprehensive cross-field validation system:

class CrossFieldValidator {
  constructor() {
    this.rules = [];
  }

  addRule(rule) {
    this.rules.push({
      name: rule.name,
      fields: rule.fields,
      validate: rule.validate,
      message: rule.message,
      severity: rule.severity || 'error'
    });
  }

  validateAll(formData) {
    const violations = [];

    this.rules.forEach(rule => {
      const result = rule.validate(formData);

      if (!result.valid) {
        violations.push({
          rule: rule.name,
          fields: rule.fields,
          message: result.message || rule.message,
          severity: rule.severity
        });
      }
    });

    return violations;
  }
}

// Define validation rules
const validator = new CrossFieldValidator();

// Date range validation
validator.addRule({
  name: 'date-range',
  fields: ['startDate', 'endDate'],
  validate: (form) => {
    if (form.endDate <= form.startDate) {
      return {
        valid: false,
        message: 'End date must be after start date'
      };
    }
    return { valid: true };
  }
});

// Budget total validation
validator.addRule({
  name: 'budget-total',
  fields: ['categories', 'budgetLimit'],
  validate: (form) => {
    const total = form.categories.reduce((sum, cat) => sum + cat.amount, 0);

    if (total > form.budgetLimit) {
      return {
        valid: false,
        message: `Total allocation ($${total}) exceeds budget limit ($${form.budgetLimit})`
      };
    }
    return { valid: true };
  }
});

// Password match validation
validator.addRule({
  name: 'password-match',
  fields: ['password', 'confirmPassword'],
  validate: (form) => {
    if (form.password !== form.confirmPassword) {
      return {
        valid: false,
        message: 'Passwords do not match'
      };
    }
    return { valid: true };
  }
});

The new system caught relationship errors immediately, with clear visual feedback showing which fields were in conflict. Booking errors dropped to zero.

Context

Cross-Field Validation applies when:

Field relationships exist: One field's validity depends on another

Totals must match: Sum of parts must equal whole

Ranges have boundaries: Start < End, Min < Max

Uniqueness across fields: Same value can't appear in multiple fields

Conditional logic: If Field A, then Field B must be...

Consistency required: Related fields must stay synchronized

Business rules span fields: Policies involve multiple values

Problem Statement

Most validation checks fields in isolation, missing relationship errors:

Independent validation only:

// Bad: Each field validated separately
function validateAge(age) {
  return age >= 0 && age <= 120;
}

function validateRetirementAge(retAge) {
  return retAge >= 55 && retAge <= 75;
}

// But what if retirement age < current age?
// What if someone is 30 and retiring at 65? Valid!
// What if someone is 70 and retiring at 60? Invalid!

No relationship checking:

// Bad: Doesn't check if total matches sum
const lineItems = [
  { price: 10.00, quantity: 2 },  // $20
  { price: 15.00, quantity: 3 }   // $45
];

const total = 70.00;  // Should be $65!
// No validation catches this

Late discovery:

// Bad: Server-side only validation
// User fills entire form
// Submits
// Server says: "End date before start date"
// User has to go back and fix

Poor error messages:

// Bad: Doesn't indicate which fields are involved
"Invalid data"  // What's invalid?
"Date error"    // Which date? Why?

No visual connection:

// Bad: Highlights one field but not the relationship
startDate.classList.add('error');
// But endDate is also part of the problem
// User doesn't see the relationship

We need validation that checks field relationships, catches conflicts early, and clearly shows which fields are involved.

Forces

Timing vs Performance

  • Real-time validation provides immediate feedback
  • But checking relationships on every keystroke is expensive
  • Balance responsiveness with performance

Completeness vs Clarity

  • Check all possible relationships thoroughly
  • But too many errors overwhelm users
  • Balance comprehensive validation with usability

Proactive vs Reactive

  • Prevent invalid combinations as user types
  • Or catch them at submission
  • Balance prevention with flexibility

Local vs Global

  • Check each relationship independently
  • Or consider all relationships together
  • Balance modularity with holistic view

Strict vs Forgiving

  • Enforce all rules rigidly
  • Or allow exceptions in edge cases
  • Balance data quality with user needs

Solution

Implement multi-field validation rules that check relationships between fields, provide clear feedback about which fields conflict and why, and integrate with form state to prevent invalid combinations proactively.

The pattern has five key strategies:

1. Declarative Relationship Rules

Define cross-field rules explicitly:

class CrossFieldRuleDefinition {
  // Date range relationships
  static dateRange(startField, endField, config = {}) {
    return {
      type: 'date-range',
      fields: [startField, endField],
      validate: (form) => {
        const start = new Date(form[startField]);
        const end = new Date(form[endField]);

        if (end <= start) {
          return {
            valid: false,
            message: config.message || `${endField} must be after ${startField}`,
            suggestion: `Set ${endField} to at least one day after ${startField}`
          };
        }

        // Optional: Check maximum duration
        if (config.maxDuration) {
          const daysDiff = (end - start) / (1000 * 60 * 60 * 24);
          if (daysDiff > config.maxDuration) {
            return {
              valid: false,
              message: `Duration cannot exceed ${config.maxDuration} days`,
              suggestion: `Reduce the date range`
            };
          }
        }

        return { valid: true };
      }
    };
  }

  // Numeric range relationships
  static numericRange(minField, maxField, config = {}) {
    return {
      type: 'numeric-range',
      fields: [minField, maxField],
      validate: (form) => {
        const min = parseFloat(form[minField]);
        const max = parseFloat(form[maxField]);

        if (max <= min) {
          return {
            valid: false,
            message: `${maxField} must be greater than ${minField}`
          };
        }

        return { valid: true };
      }
    };
  }

  // Sum equals total
  static sumEquals(itemsField, totalField, config = {}) {
    return {
      type: 'sum-equals',
      fields: [itemsField, totalField],
      validate: (form) => {
        const items = form[itemsField] || [];
        const expectedTotal = items.reduce((sum, item) => {
          return sum + (item.amount || 0);
        }, 0);

        const actualTotal = parseFloat(form[totalField]);
        const tolerance = config.tolerance || 0.01;

        if (Math.abs(actualTotal - expectedTotal) > tolerance) {
          return {
            valid: false,
            message: `${totalField} ($${actualTotal.toFixed(2)}) doesn't match sum of items ($${expectedTotal.toFixed(2)})`,
            expectedValue: expectedTotal,
            actualValue: actualTotal,
            suggestion: `Update ${totalField} to $${expectedTotal.toFixed(2)}`
          };
        }

        return { valid: true };
      }
    };
  }

  // Fields must match
  static mustMatch(field1, field2, config = {}) {
    return {
      type: 'must-match',
      fields: [field1, field2],
      validate: (form) => {
        if (form[field1] !== form[field2]) {
          return {
            valid: false,
            message: config.message || `${field1} and ${field2} must match`
          };
        }

        return { valid: true };
      }
    };
  }

  // Fields must be different
  static mustDiffer(field1, field2, config = {}) {
    return {
      type: 'must-differ',
      fields: [field1, field2],
      validate: (form) => {
        if (form[field1] === form[field2]) {
          return {
            valid: false,
            message: config.message || `${field1} and ${field2} must be different`
          };
        }

        return { valid: true };
      }
    };
  }

  // Conditional requirement
  static conditionalRequired(triggerField, requiredField, condition) {
    return {
      type: 'conditional-required',
      fields: [triggerField, requiredField],
      validate: (form) => {
        const shouldBeRequired = condition(form[triggerField]);

        if (shouldBeRequired && !form[requiredField]) {
          return {
            valid: false,
            message: `${requiredField} is required when ${triggerField} is ${form[triggerField]}`
          };
        }

        return { valid: true };
      }
    };
  }

  // Custom relationship
  static custom(fields, validateFn, message) {
    return {
      type: 'custom',
      fields: fields,
      validate: (form) => {
        const valid = validateFn(form);

        if (!valid) {
          return {
            valid: false,
            message: message
          };
        }

        return { valid: true };
      }
    };
  }
}

2. Relationship Validator

Execute validation rules:

class RelationshipValidator {
  constructor() {
    this.rules = [];
    this.violations = new Map();
  }

  addRule(rule) {
    this.rules.push(rule);
  }

  validate(formData, options = {}) {
    const violations = [];

    this.rules.forEach(rule => {
      // Skip if not all required fields present
      if (!this.hasRequiredFields(rule.fields, formData)) {
        return;
      }

      const result = rule.validate(formData);

      if (!result.valid) {
        violations.push({
          type: rule.type,
          fields: rule.fields,
          message: result.message,
          suggestion: result.suggestion,
          expectedValue: result.expectedValue,
          actualValue: result.actualValue,
          severity: result.severity || 'error'
        });
      }
    });

    // Store violations for later reference
    this.violations.clear();
    violations.forEach(v => {
      v.fields.forEach(field => {
        if (!this.violations.has(field)) {
          this.violations.set(field, []);
        }
        this.violations.get(field).push(v);
      });
    });

    return violations;
  }

  hasRequiredFields(fields, formData) {
    return fields.every(field => {
      // Handle nested fields (e.g., 'items[0].amount')
      return this.getNestedValue(formData, field) !== undefined;
    });
  }

  getNestedValue(obj, path) {
    return path.split('.').reduce((current, key) => {
      return current?.[key];
    }, obj);
  }

  getViolationsForField(fieldName) {
    return this.violations.get(fieldName) || [];
  }

  clearViolations() {
    this.violations.clear();
  }
}

3. Visual Relationship Feedback

Show which fields are related in the error:

class RelationshipFeedback {
  showViolation(violation) {
    // Highlight all involved fields
    violation.fields.forEach(field => {
      this.highlightField(field, 'error');
    });

    // Draw visual connection between fields
    if (violation.fields.length === 2) {
      this.connectFields(violation.fields[0], violation.fields[1]);
    }

    // Show error message near the fields
    this.showErrorMessage(violation);
  }

  highlightField(fieldName, type = 'error') {
    const field = document.getElementById(fieldName);
    const container = field?.closest('.field-container');

    if (container) {
      container.classList.add(`validation-${type}`);
      field.setAttribute('aria-invalid', 'true');
    }
  }

  connectFields(field1Name, field2Name) {
    const field1 = document.getElementById(field1Name);
    const field2 = document.getElementById(field2Name);

    if (!field1 || !field2) return;

    // Create SVG connector
    const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
    svg.classList.add('field-connector');
    svg.style.position = 'absolute';
    svg.style.pointerEvents = 'none';
    svg.style.zIndex = '1000';

    // Calculate positions
    const rect1 = field1.getBoundingClientRect();
    const rect2 = field2.getBoundingClientRect();

    const x1 = rect1.right;
    const y1 = rect1.top + rect1.height / 2;
    const x2 = rect2.left;
    const y2 = rect2.top + rect2.height / 2;

    // Draw curved line
    const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
    const midX = (x1 + x2) / 2;
    path.setAttribute('d', `M ${x1},${y1} Q ${midX},${y1} ${midX},${(y1+y2)/2} T ${x2},${y2}`);
    path.setAttribute('stroke', '#e74c3c');
    path.setAttribute('stroke-width', '2');
    path.setAttribute('fill', 'none');

    svg.appendChild(path);
    document.body.appendChild(svg);

    // Remove after delay
    setTimeout(() => svg.remove(), 5000);
  }

  showErrorMessage(violation) {
    // Find best position for error message
    const primaryField = document.getElementById(violation.fields[0]);
    const container = primaryField?.closest('.field-container');

    if (!container) return;

    // Create error element
    const error = document.createElement('div');
    error.className = 'cross-field-error';
    error.innerHTML = `
      <div class="error-icon">⚠️</div>
      <div class="error-content">
        <div class="error-message">${violation.message}</div>
        ${violation.suggestion ? `
          <div class="error-suggestion">
            💡 ${violation.suggestion}
          </div>
        ` : ''}
        <div class="error-fields">
          Affects: ${violation.fields.join(', ')}
        </div>
      </div>
    `;

    container.appendChild(error);
  }

  clearViolations() {
    document.querySelectorAll('.validation-error').forEach(el => {
      el.classList.remove('validation-error');
    });

    document.querySelectorAll('.cross-field-error').forEach(el => {
      el.remove();
    });

    document.querySelectorAll('.field-connector').forEach(el => {
      el.remove();
    });
  }
}

4. Real-Time Relationship Checking

Validate relationships as user types:

class RealTimeRelationshipChecker {
  constructor(validator, feedback) {
    this.validator = validator;
    this.feedback = feedback;
    this.debounceTimers = new Map();
  }

  setupListeners() {
    // Listen to all form inputs
    document.querySelectorAll('input, select, textarea').forEach(input => {
      input.addEventListener('input', (e) => {
        this.handleInput(e.target.name);
      });

      input.addEventListener('change', (e) => {
        this.handleChange(e.target.name);
      });
    });
  }

  handleInput(fieldName) {
    // Debounce input validation
    if (this.debounceTimers.has(fieldName)) {
      clearTimeout(this.debounceTimers.get(fieldName));
    }

    const timer = setTimeout(() => {
      this.validateField(fieldName);
    }, 500);  // Wait 500ms after typing stops

    this.debounceTimers.set(fieldName, timer);
  }

  handleChange(fieldName) {
    // Immediate validation on change (blur, selection)
    this.validateField(fieldName);
  }

  validateField(fieldName) {
    // Clear previous violations for this field
    this.feedback.clearViolations();

    // Get form data
    const formData = this.getFormData();

    // Validate all rules
    const violations = this.validator.validate(formData);

    // Show violations
    violations.forEach(violation => {
      this.feedback.showViolation(violation);
    });
  }

  getFormData() {
    const form = document.querySelector('form');
    const data = new FormData(form);
    return Object.fromEntries(data.entries());
  }
}

5. Auto-Fix Suggestions

Offer to fix relationship violations:

class AutoFixEngine {
  constructor(validator) {
    this.validator = validator;
  }

  suggestFixes(violations, formData) {
    const fixes = [];

    violations.forEach(violation => {
      const fix = this.generateFix(violation, formData);
      if (fix) {
        fixes.push(fix);
      }
    });

    return fixes;
  }

  generateFix(violation, formData) {
    switch(violation.type) {
      case 'sum-equals':
        return this.fixSumMismatch(violation, formData);

      case 'date-range':
        return this.fixDateRange(violation, formData);

      case 'must-match':
        return this.fixMismatch(violation, formData);

      default:
        return null;
    }
  }

  fixSumMismatch(violation, formData) {
    if (violation.expectedValue !== undefined) {
      return {
        description: `Update total to ${violation.expectedValue}`,
        apply: () => {
          const totalField = violation.fields.find(f => f.includes('total'));
          if (totalField) {
            document.getElementById(totalField).value = violation.expectedValue;
          }
        }
      };
    }
  }

  fixDateRange(violation, formData) {
    const [startField, endField] = violation.fields;
    const start = new Date(formData[startField]);

    return {
      description: `Set end date to one day after start date`,
      apply: () => {
        const newEnd = new Date(start);
        newEnd.setDate(newEnd.getDate() + 1);

        const endInput = document.getElementById(endField);
        endInput.value = newEnd.toISOString().split('T')[0];
      }
    };
  }

  fixMismatch(violation, formData) {
    const [field1, field2] = violation.fields;

    return {
      description: `Copy ${field1} to ${field2}`,
      apply: () => {
        const value = document.getElementById(field1).value;
        document.getElementById(field2).value = value;
      }
    };
  }

  presentFixOptions(fixes) {
    if (fixes.length === 0) return;

    const modal = document.createElement('div');
    modal.className = 'fix-suggestions-modal';
    modal.innerHTML = `
      <div class="modal-header">
        <h3>Auto-Fix Suggestions</h3>
      </div>
      <div class="modal-body">
        <p>We found some issues that can be fixed automatically:</p>
        <ul class="fix-list">
          ${fixes.map((fix, index) => `
            <li>
              <button onclick="applyFix(${index})">
                ${fix.description}
              </button>
            </li>
          `).join('')}
        </ul>
      </div>
      <div class="modal-footer">
        <button onclick="closeModal()">Cancel</button>
        <button onclick="applyAllFixes()">Apply All</button>
      </div>
    `;

    document.body.appendChild(modal);
  }
}

Implementation Details

Complete Cross-Field Validation System

class CrossFieldValidationSystem {
  constructor() {
    this.validator = new RelationshipValidator();
    this.feedback = new RelationshipFeedback();
    this.checker = new RealTimeRelationshipChecker(this.validator, this.feedback);
    this.autoFix = new AutoFixEngine(this.validator);
  }

  initialize(formDefinition) {
    // Define all relationship rules
    formDefinition.relationships?.forEach(rel => {
      const rule = this.createRule(rel);
      this.validator.addRule(rule);
    });

    // Setup real-time checking
    this.checker.setupListeners();
  }

  createRule(relationship) {
    const RuleDef = CrossFieldRuleDefinition;

    switch(relationship.type) {
      case 'date-range':
        return RuleDef.dateRange(
          relationship.startField,
          relationship.endField,
          relationship.config
        );

      case 'sum-equals':
        return RuleDef.sumEquals(
          relationship.itemsField,
          relationship.totalField,
          relationship.config
        );

      case 'must-match':
        return RuleDef.mustMatch(
          relationship.field1,
          relationship.field2,
          relationship.config
        );

      case 'must-differ':
        return RuleDef.mustDiffer(
          relationship.field1,
          relationship.field2,
          relationship.config
        );

      case 'custom':
        return RuleDef.custom(
          relationship.fields,
          relationship.validate,
          relationship.message
        );

      default:
        throw new Error(`Unknown relationship type: ${relationship.type}`);
    }
  }

  validateForm() {
    const formData = this.getFormData();
    const violations = this.validator.validate(formData);

    if (violations.length > 0) {
      // Show violations
      violations.forEach(v => {
        this.feedback.showViolation(v);
      });

      // Offer auto-fixes
      const fixes = this.autoFix.suggestFixes(violations, formData);
      if (fixes.length > 0) {
        this.autoFix.presentFixOptions(fixes);
      }

      return false;
    }

    return true;
  }

  getFormData() {
    const form = document.querySelector('form');
    const data = new FormData(form);
    return Object.fromEntries(data.entries());
  }
}

// Example usage
const system = new CrossFieldValidationSystem();

system.initialize({
  relationships: [
    {
      type: 'date-range',
      startField: 'startDate',
      endField: 'endDate',
      config: { maxDuration: 365 }
    },
    {
      type: 'must-match',
      field1: 'password',
      field2: 'confirmPassword',
      config: { message: 'Passwords must match' }
    },
    {
      type: 'must-differ',
      field1: 'primaryContact',
      field2: 'emergencyContact',
      config: { message: 'Emergency contact must be different from primary' }
    },
    {
      type: 'sum-equals',
      itemsField: 'lineItems',
      totalField: 'subtotal'
    },
    {
      type: 'custom',
      fields: ['age', 'retirementAge'],
      validate: (form) => {
        return parseInt(form.retirementAge) > parseInt(form.age);
      },
      message: 'Retirement age must be greater than current age'
    }
  ]
});

Consequences

Benefits

Catch Relationship Errors: - Impossible combinations prevented - Business rules enforced - Data integrity maintained

Better User Feedback: - Clear which fields are involved - Visual connection between related fields - Helpful suggestions for fixes

Improved Data Quality: - Consistent relationships - No orphaned data - Validated totals

Reduced Support Burden: - Fewer "why was my submission rejected" questions - Self-explanatory error messages - Auto-fix reduces frustration

Real-Time Prevention: - Catch errors as they're made - Don't wait for submission - Faster error resolution

Liabilities

Performance Cost: - Checking relationships continuously is expensive - Complex forms can get slow - Need optimization and debouncing

Complex Error States: - Multiple violations can overwhelm - Prioritization needed - Clear presentation challenging

False Positives: - Rules may be too strict - Edge cases not handled - Need flexibility for exceptions

Implementation Complexity: - Many relationship types - Testing all combinations hard - Maintenance burden grows

User Confusion: - "Why are these fields connected?" - Need clear explanations - Visual feedback must be obvious

Domain Examples

Financial: Budget Management

// Budget allocation cross-field validation
defineBudgetRules() {
  // Sum of categories must equal total budget
  this.validator.addRule(
    CrossFieldRuleDefinition.sumEquals('categories', 'totalBudget')
  );

  // No category can exceed 50% of total
  this.validator.addRule(
    CrossFieldRuleDefinition.custom(
      ['categories', 'totalBudget'],
      (form) => {
        const max = form.totalBudget * 0.5;
        return form.categories.every(cat => cat.amount <= max);
      },
      'No single category can exceed 50% of total budget'
    )
  );
}

Healthcare: Prescription Validation

// Medication dosage cross-field validation
definePrescriptionRules() {
  // Daily dose cannot exceed maximum
  this.validator.addRule({
    type: 'custom',
    fields: ['dosageAmount', 'frequency', 'maxDailyDose'],
    validate: (form) => {
      const daily = form.dosageAmount * form.frequency;
      return daily <= form.maxDailyDose;
    },
    message: 'Daily dose exceeds maximum safe dosage'
  });

  // Refill date must be after prescription date
  this.validator.addRule(
    CrossFieldRuleDefinition.dateRange('prescriptionDate', 'refillDate')
  );
}

E-commerce: Order Validation

// Order total cross-field validation
defineOrderRules() {
  // Subtotal = sum of line items
  this.validator.addRule(
    CrossFieldRuleDefinition.sumEquals('lineItems', 'subtotal')
  );

  // Discount cannot exceed subtotal
  this.validator.addRule({
    type: 'custom',
    fields: ['subtotal', 'discount'],
    validate: (form) => form.discount <= form.subtotal,
    message: 'Discount cannot exceed subtotal'
  });

  // Total = subtotal - discount + tax + shipping
  this.validator.addRule({
    type: 'custom',
    fields: ['subtotal', 'discount', 'tax', 'shipping', 'total'],
    validate: (form) => {
      const expected = form.subtotal - form.discount + form.tax + form.shipping;
      return Math.abs(form.total - expected) < 0.01;
    },
    message: 'Total does not match calculation'
  });
}

Prerequisites: - Volume 3, Pattern 6: Domain-Aware Validation (individual field validation)

Synergies: - Volume 3, Pattern 7: Calculated Dependencies (relationships drive calculations) - Volume 3, Pattern 9: Contextual Constraints (context affects valid relationships) - Volume 3, Pattern 11: Cascading Updates (changing one field affects others) - Volume 3, Pattern 13: Conditional Requirements (requirements affect validation)

Conflicts: - Extreme performance requirements (continuous checking too expensive) - Offline forms (can't do real-time validation)

Alternatives: - Server-side only (validate on submit) - Prevent invalid entry (disable conflicting options) - Guided workflows (step-by-step prevents conflicts)

Known Uses

Tax Software: Deductions can't exceed income, dependent ages, marriage date validation

Booking Systems: Check-out after check-in, room availability vs capacity

Financial Applications: Transaction amounts, account balances, payment totals

Healthcare Forms: Medication interactions, dosage calculations, appointment scheduling

E-commerce: Cart totals, shipping calculations, inventory availability

Project Management: Task dependencies, resource allocation, timeline validation

HR Systems: Salary ranges, tenure calculations, benefit eligibility


Further Reading

Academic Foundations

  • Integrity Constraints: Date, C.J. (2004). An Introduction to Database Systems (8th ed.). Addison-Wesley. ISBN: 978-0321197849 - Chapter on constraints
  • Form Validation: Wroblewski, L. (2008). Web Form Design: Filling in the Blanks. Rosenfeld Media. ISBN: 978-1933820241
  • Error Prevention: Norman, D.A. (2013). The Design of Everyday Things (Revised ed.). Basic Books. ISBN: 978-0465050659

Practical Implementation

Standards & Best Practices

  • Pattern 11: Validation Rules - Cross-field as advanced validation
  • Pattern 12: Calculated Dependencies - Calculations inform validation
  • Pattern 16: Cascading Updates - Updates trigger re-validation
  • Pattern 18: Conditional Requirements - Validate conditional fields together
  • Pattern 20: Real-Time Feedback - When to show cross-field errors
  • Volume 2, Chapter 4: Interaction Outcome Classification - Validation quality metrics

Tools & Libraries

Implementation Examples