Pattern 9: Contextual Constraints
Part II: Interaction Patterns - Intelligence Patterns
Opening Scenario: The Date Field That Couldn't Decide
David was building a scheduling system for a medical practice. He started with what seemed like a simple field:
<label>Date:</label>
<input type="date" name="appointmentDate">
The practice manager, Linda, tested it and immediately found problems.
"David, I'm trying to schedule Mr. Johnson's follow-up appointment for next Tuesday, but the system won't let me select it."
David looked at his validation code:
function validateDate(date) {
const selected = new Date(date);
const today = new Date();
// Date must be in the past
if (selected > today) {
return { valid: false, message: "Date cannot be in the future" };
}
return { valid: true };
}
"Oh," David said. "I wrote this for the patient intake form, where we ask for birth date. Birth dates have to be in the past. But you're scheduling an appointment, which has to be in the future."
"There's more," Linda said. She showed him the medical history form. "When documenting a patient's previous surgery, I need to enter a date from years ago. But when scheduling a lab test, I can only book certain days based on what tests the lab runs."
David realized: the same field - a date - had completely different constraints depending on context:
Birth date: Must be in the past, can't be future, reasonably within human lifespan Appointment date: Must be in the future, can't be past, only on business days, only when doctor available Medical history date: Any date in the past, but not before patient was born Lab test date: Must be in the future, only specific days (lab runs different tests different days), no more than 90 days out Prescription date: Today or recent past (within 30 days for controlled substances) Insurance effective date: Could be past, present, or future, but must align with policy period
Six months later, David showed me the redesigned system:
class ContextualDateValidator {
validate(date, context) {
const constraints = this.getConstraints(context);
const selected = new Date(date);
const today = new Date();
// Apply contextual constraints
const results = [];
// Temporal direction
if (constraints.mustBeFuture && selected <= today) {
return {
valid: false,
message: `${context.fieldLabel} must be in the future`,
suggestion: this.getNextValidDate(context)
};
}
if (constraints.mustBePast && selected >= today) {
return {
valid: false,
message: `${context.fieldLabel} must be in the past`
};
}
// Business rules
if (constraints.businessDaysOnly && this.isWeekend(selected)) {
return {
valid: false,
message: "Please select a business day (Monday-Friday)",
suggestion: this.getNextBusinessDay(selected)
};
}
// Availability constraints
if (constraints.checkAvailability) {
const available = this.checkResourceAvailability(
selected,
context.resourceId,
context.resourceType
);
if (!available.isAvailable) {
return {
valid: false,
message: available.reason,
suggestions: available.alternativeDates
};
}
}
// Domain-specific constraints
if (constraints.labTestDay) {
const validDays = this.getLabTestDays(context.testType);
const dayOfWeek = selected.getDay();
if (!validDays.includes(dayOfWeek)) {
return {
valid: false,
message: `${context.testType} is only available on ${this.formatDays(validDays)}`,
suggestions: this.getNextValidLabDays(selected, validDays)
};
}
}
// Relative constraints (compared to other dates)
if (constraints.mustBeAfter) {
const compareDate = new Date(context[constraints.mustBeAfter]);
if (selected <= compareDate) {
return {
valid: false,
message: `Must be after ${constraints.mustBeAfter} (${this.formatDate(compareDate)})`
};
}
}
return { valid: true };
}
getConstraints(context) {
const constraintMap = {
'birthDate': {
mustBePast: true,
maxYearsAgo: 120,
minYearsAgo: 0,
businessDaysOnly: false
},
'appointmentDate': {
mustBeFuture: true,
businessDaysOnly: true,
checkAvailability: true,
resourceType: 'provider',
minDaysOut: 0,
maxDaysOut: 365
},
'labTestDate': {
mustBeFuture: true,
labTestDay: true,
testType: context.testType,
maxDaysOut: 90
},
'medicalHistoryDate': {
mustBePast: true,
mustBeAfter: 'birthDate',
maxYearsAgo: null // Any date after birth
},
'prescriptionDate': {
allowToday: true,
maxDaysAgo: 30, // Controlled substances
mustNotBeFuture: true
}
};
return constraintMap[context.fieldType] || {};
}
}
Now the same date field had intelligent, context-aware constraints. The form knew: - WHO was using it (doctor can backdate prescriptions within limits, nurse cannot) - WHAT it was for (appointment vs medical history vs lab test) - WHEN it was being used (relative to other dates in the form) - WHY the date was needed (different policies for different purposes)
David's system went from rigid, one-size-fits-all validation to flexible, intelligent constraints that adapted to context.
Context
Contextual Constraints applies when:
Same field, different rules: Validation depends on purpose, user, or situation
Context is knowable: System has enough information to determine appropriate constraints
User role matters: Different users have different permissions and requirements
Purpose varies: Same data type used for different reasons
Temporal context relevant: When something happens affects what's valid
Relationships matter: Constraints depend on other fields or data
Business rules complex: Simple validation insufficient for real-world requirements
Problem Statement
Most validation applies the same rules regardless of context:
Ignoring user role:
// Bad: Same amount limit for everyone
function validateExpenseAmount(amount) {
return amount <= 1000; // $1,000 limit
}
// Staff can expense up to $500
// Managers can expense up to $5,000
// Executives can expense up to $25,000
// But system treats everyone the same!
Missing purpose:
// Bad: Email always required
function validateEmail(email) {
if (!email) {
return { valid: false, message: "Email is required" };
}
return { valid: true };
}
// But:
// - Required for account creation
// - Optional for guest checkout
// - Not needed for in-person registration
No temporal awareness:
// Bad: Static date validation
function validateDate(date) {
const d = new Date(date);
return d > new Date('1900-01-01') && d < new Date('2100-12-31');
}
// Accepts birth dates in the future
// Accepts appointment dates in the past
// No awareness of context
Ignoring relationships:
// Bad: Independent field validation
function validateEndDate(endDate) {
return endDate instanceof Date && !isNaN(endDate);
}
// Doesn't check that end date is after start date
// Doesn't verify duration is reasonable
// No relationship awareness
One-size-fits-all:
// Bad: Same phone validation everywhere
function validatePhone(phone) {
return /^\d{10}$/.test(phone);
}
// But:
// - Personal phone: US format
// - International client: various formats
// - Fax number: may include extension
// - Emergency contact: require area code
We need constraints that adapt based on who is using the form, what they're trying to do, when they're doing it, and why.
Forces
Flexibility vs Consistency
- Context-aware rules provide appropriate validation
- But too much variation confuses users
- Balance adaptation with predictability
Complexity vs Maintainability
- Contextual logic can become complex
- Many if/else branches hard to maintain
- Need systematic approach to context handling
Security vs Usability
- More permissive rules for trusted users
- But can't compromise security
- Balance convenience with protection
Explicit vs Implicit Context
- Some context is obvious (user role)
- Some must be inferred (purpose from workflow)
- Decision: ask user or detect automatically?
Performance vs Freshness
- Checking availability is slow
- Cached rules may be stale
- Balance speed with accuracy
Solution
Define validation constraints that adapt based on context dimensions (who, what, when, where, why), with clear priority rules for resolving conflicts and transparent explanations for users.
The pattern has four key context dimensions:
1. Actor Context (WHO)
Constraints based on user identity, role, and permissions:
class ActorContext {
getConstraints(fieldType, user) {
const roleConstraints = {
'staff': {
expenseLimit: 500,
approvalRequired: true,
canBackdate: false,
editWindow: 7 // days
},
'manager': {
expenseLimit: 5000,
approvalRequired: false,
canBackdate: 7, // days
editWindow: 30
},
'executive': {
expenseLimit: 25000,
approvalRequired: false,
canBackdate: 30,
editWindow: 90
},
'finance': {
expenseLimit: null, // No limit
approvalRequired: false,
canBackdate: 365,
editWindow: null // Unlimited
}
};
const baseConstraints = roleConstraints[user.role] || roleConstraints['staff'];
// Apply additional constraints based on user attributes
if (user.isTraining) {
return {
...baseConstraints,
expenseLimit: Math.min(baseConstraints.expenseLimit, 100),
approvalRequired: true
};
}
if (user.hasViolations) {
return {
...baseConstraints,
approvalRequired: true,
editWindow: 0 // No editing
};
}
return baseConstraints;
}
}
2. Purpose Context (WHAT & WHY)
Constraints based on what the data will be used for:
class PurposeContext {
getConstraints(fieldType, purpose) {
const purposeConstraints = {
'email': {
'account_creation': {
required: true,
mustVerify: true,
allowDisposable: false,
corporateOnly: false
},
'guest_checkout': {
required: false,
mustVerify: false,
allowDisposable: true,
corporateOnly: false
},
'employee_onboarding': {
required: true,
mustVerify: true,
allowDisposable: false,
corporateOnly: true // Must be company domain
},
'newsletter_signup': {
required: true,
mustVerify: false,
allowDisposable: true,
corporateOnly: false
}
},
'phone': {
'emergency_contact': {
required: true,
mustBeDifferent: true, // Can't be user's own number
allowInternational: true,
verifyCarrier: false
},
'appointment_reminder': {
required: false,
mustBeMobile: true, // For SMS
allowLandline: false,
verifyCarrier: true
},
'billing_contact': {
required: true,
mustBeMobile: false,
allowLandline: true,
verifyCarrier: false
}
}
};
return purposeConstraints[fieldType]?.[purpose] || {};
}
}
3. Temporal Context (WHEN)
Constraints based on timing and relationships to other dates:
class TemporalContext {
getConstraints(fieldType, temporalContext) {
const now = new Date();
const constraints = {
'appointmentDate': {
direction: 'future',
minDaysFromNow: temporalContext.urgency === 'routine' ? 1 : 0,
maxDaysFromNow: 365,
businessDaysOnly: true,
excludeHolidays: true,
checkAvailability: true
},
'birthDate': {
direction: 'past',
minYearsAgo: 0,
maxYearsAgo: 120,
mustBeBefore: now
},
'hireDate': {
direction: 'either', // Can be past or future
mustBeAfter: 'birthDate',
mustNotExceed: { years: 16, from: 'birthDate' }, // Must be 16+
businessDaysOnly: true
},
'effectiveDate': {
direction: 'either',
mustBeWithin: {
field: 'policyPeriod',
type: 'range'
}
}
};
return constraints[fieldType] || {};
}
validateTemporal(date, constraints, relatedDates = {}) {
const selected = new Date(date);
const now = new Date();
// Direction constraints
if (constraints.direction === 'future' && selected <= now) {
return {
valid: false,
message: "Date must be in the future"
};
}
if (constraints.direction === 'past' && selected >= now) {
return {
valid: false,
message: "Date must be in the past"
};
}
// Relative constraints
if (constraints.mustBeAfter) {
const compareDate = relatedDates[constraints.mustBeAfter];
if (compareDate && selected <= new Date(compareDate)) {
return {
valid: false,
message: `Must be after ${constraints.mustBeAfter}`
};
}
}
// Duration constraints
if (constraints.mustNotExceed) {
const fromDate = new Date(relatedDates[constraints.mustNotExceed.from]);
const yearsDiff = (selected - fromDate) / (365.25 * 24 * 60 * 60 * 1000);
if (yearsDiff < constraints.mustNotExceed.years) {
return {
valid: false,
message: `Must be at least ${constraints.mustNotExceed.years} years after ${constraints.mustNotExceed.from}`
};
}
}
return { valid: true };
}
}
4. Situational Context (WHERE & Compound)
Constraints based on location, workflow state, and combined factors:
class SituationalContext {
getConstraints(fieldType, situation) {
// Geographic context
if (situation.location) {
return this.getGeographicConstraints(fieldType, situation.location);
}
// Workflow state context
if (situation.workflowState) {
return this.getWorkflowConstraints(fieldType, situation.workflowState);
}
// Compound context
return this.getCompoundConstraints(fieldType, situation);
}
getGeographicConstraints(fieldType, location) {
if (fieldType === 'phone') {
const formats = {
'US': { pattern: /^\d{10}$/, format: '(XXX) XXX-XXXX' },
'UK': { pattern: /^\d{11}$/, format: 'XXXXX XXXXXX' },
'FR': { pattern: /^\d{10}$/, format: 'XX XX XX XX XX' }
};
return formats[location.country] || formats['US'];
}
if (fieldType === 'postalCode') {
const formats = {
'US': { pattern: /^\d{5}(-\d{4})?$/, name: 'ZIP code' },
'CA': { pattern: /^[A-Z]\d[A-Z] \d[A-Z]\d$/, name: 'Postal code' },
'UK': { pattern: /^[A-Z]{1,2}\d{1,2} \d[A-Z]{2}$/, name: 'Postcode' }
};
return formats[location.country] || {};
}
}
getWorkflowConstraints(fieldType, workflowState) {
// Different constraints based on where in the workflow
const stateConstraints = {
'draft': {
required: false,
canEdit: true,
validationLevel: 'warning'
},
'submitted': {
required: true,
canEdit: false,
validationLevel: 'error'
},
'approved': {
required: true,
canEdit: false,
validationLevel: 'error',
auditTrail: true
}
};
return stateConstraints[workflowState] || stateConstraints['draft'];
}
getCompoundConstraints(fieldType, situation) {
// Combine multiple context factors
const constraints = {};
// Example: Shipping address validation
if (fieldType === 'shippingAddress') {
constraints.required = situation.orderType !== 'digital';
constraints.international = situation.customerType === 'international';
constraints.verifyWithCarrier = situation.orderValue > 1000;
constraints.requireSignature = situation.orderValue > 5000;
}
return constraints;
}
}
Implementation Details
Complete Contextual Constraint Engine
class ContextualConstraintEngine {
constructor() {
this.actorContext = new ActorContext();
this.purposeContext = new PurposeContext();
this.temporalContext = new TemporalContext();
this.situationalContext = new SituationalContext();
}
async validate(field, value, context) {
// Gather constraints from all context dimensions
const constraints = await this.gatherConstraints(field, context);
// Apply constraints in priority order
const results = await this.applyConstraints(value, constraints, context);
// Return combined result
return this.combineResults(results, constraints);
}
async gatherConstraints(field, context) {
const constraints = {
actor: this.actorContext.getConstraints(field.type, context.user),
purpose: this.purposeContext.getConstraints(field.type, context.purpose),
temporal: this.temporalContext.getConstraints(field.type, context.temporal),
situational: this.situationalContext.getConstraints(field.type, context.situation)
};
// Merge constraints with priority rules
return this.mergeConstraints(constraints, field.type);
}
mergeConstraints(constraints, fieldType) {
// Priority order: actor > purpose > temporal > situational
// More restrictive wins when conflicts
const merged = {};
// Start with least restrictive (situational)
Object.assign(merged, constraints.situational);
// Layer on temporal
Object.assign(merged, constraints.temporal);
// Layer on purpose
Object.assign(merged, constraints.purpose);
// Layer on actor (highest priority)
Object.assign(merged, constraints.actor);
// Resolve conflicts (take most restrictive)
if (merged.expenseLimit !== undefined && constraints.purpose.expenseLimit !== undefined) {
merged.expenseLimit = Math.min(
merged.expenseLimit || Infinity,
constraints.purpose.expenseLimit || Infinity
);
}
if (merged.required !== undefined) {
// If any context says required, it's required
merged.required =
constraints.actor.required ||
constraints.purpose.required ||
constraints.situational.required;
}
return merged;
}
async applyConstraints(value, constraints, context) {
const results = [];
// Required check
if (constraints.required && !value) {
results.push({
valid: false,
constraint: 'required',
message: this.getRequiredMessage(constraints, context),
severity: 'error'
});
return results; // Stop if required and empty
}
// Type-specific validation
if (value) {
// Amount constraints
if (constraints.minAmount !== undefined && value < constraints.minAmount) {
results.push({
valid: false,
constraint: 'minAmount',
message: `Amount must be at least ${this.formatCurrency(constraints.minAmount)}`,
severity: 'error'
});
}
if (constraints.maxAmount !== undefined && value > constraints.maxAmount) {
results.push({
valid: false,
constraint: 'maxAmount',
message: `Amount cannot exceed ${this.formatCurrency(constraints.maxAmount)}`,
severity: 'error',
explanation: this.getExplanation('maxAmount', constraints, context)
});
}
// Approval constraints
if (constraints.requiresApproval && value > constraints.approvalThreshold) {
results.push({
valid: true,
constraint: 'requiresApproval',
message: 'This amount requires manager approval',
severity: 'info'
});
}
}
return results;
}
getRequiredMessage(constraints, context) {
if (context.purpose === 'account_creation') {
return 'Email is required to create an account';
}
if (context.purpose === 'emergency_contact') {
return 'Emergency contact phone is required for patient safety';
}
return 'This field is required';
}
getExplanation(constraint, constraints, context) {
const explanations = {
'maxAmount': () => {
if (context.user.role === 'staff') {
return `Staff members can expense up to ${this.formatCurrency(constraints.maxAmount)}. Amounts above this require manager approval.`;
}
return null;
},
'mustBeFuture': () => {
if (context.purpose === 'appointment') {
return 'Appointments must be scheduled for future dates. For backdating, contact your supervisor.';
}
return null;
}
};
const explainer = explanations[constraint];
return explainer ? explainer() : null;
}
combineResults(results, constraints) {
const errors = results.filter(r => !r.valid && r.severity === 'error');
if (errors.length > 0) {
return {
valid: false,
message: errors[0].message,
explanation: errors[0].explanation,
allErrors: errors
};
}
const warnings = results.filter(r => r.severity === 'warning');
const info = results.filter(r => r.severity === 'info');
return {
valid: true,
warnings: warnings,
info: info,
appliedConstraints: constraints
};
}
}
// Usage
const engine = new ContextualConstraintEngine();
const result = await engine.validate(
{ type: 'expenseAmount', name: 'amount' },
1500,
{
user: { role: 'staff', id: 'user123' },
purpose: 'travel_expense',
temporal: { dateIncurred: '2024-12-20' },
situation: { workflowState: 'draft' }
}
);
if (!result.valid) {
console.log(result.message);
if (result.explanation) {
console.log(result.explanation);
}
}
Consequences
Benefits
Appropriate Validation: - Rules match the actual requirement - Users not blocked by overly restrictive validation - Security maintained where needed
Better User Experience: - Constraints make sense in context - Clear explanations for restrictions - Helpful suggestions for alternatives
Encodes Business Rules: - Complex policies implemented correctly - Role-based permissions enforced - Compliance requirements met
Reduces Errors: - Context-appropriate defaults - Catches mistakes early - Prevents invalid combinations
Flexibility: - Same field serves multiple purposes - Adapts to changing requirements - Scales with organizational complexity
Liabilities
Implementation Complexity: - Many conditional branches - Context must be gathered and passed - Priority rules can conflict
Testing Challenges: - Must test all context combinations - Edge cases multiply - Regression testing complex
Performance Concerns: - Checking availability is slow - Multiple validation layers add latency - Caching helps but adds complexity
User Confusion: - Same field, different rules can surprise - Need clear communication - Transparency essential
Maintenance Burden: - Business rules change - Context logic grows over time - Documentation critical
Domain Examples
Healthcare: Appointment Scheduling
const appointmentConstraints = {
actor: (user) => ({
canSchedule: ['front_desk', 'nurse', 'doctor'].includes(user.role),
canBackdate: user.role === 'doctor',
maxDaysOut: user.role === 'doctor' ? 365 : 90
}),
purpose: (appointmentType) => ({
'new_patient': {
minDuration: 60, // minutes
requiresSpecialty: true,
businessDaysOnly: true
},
'follow_up': {
minDuration: 30,
requiresSpecialty: false,
businessDaysOnly: false // Can book weekends for urgent follow-up
},
'urgent': {
minDuration: 15,
sameDayAllowed: true,
overrideAvailability: true
}
}),
temporal: (context) => ({
minDaysFromNow: context.appointmentType === 'urgent' ? 0 : 1,
maxDaysFromNow: 365,
excludeDates: context.providerVacation,
mustBeAfter: context.lastVisit ? addDays(context.lastVisit, 7) : null
})
};
Financial: Transaction Limits
const transactionConstraints = {
actor: (user) => ({
dailyLimit: {
'customer': 5000,
'business': 25000,
'premium': 100000
}[user.accountType],
requiresVerification: user.accountAge < 30, // days
requiresTwoFactor: user.riskScore > 50
}),
purpose: (transactionType) => ({
'wire_transfer': {
minAmount: 100,
maxAmount: 50000,
businessDaysOnly: true,
cutoffTime: '15:00' // 3 PM
},
'ach_transfer': {
minAmount: 1,
maxAmount: 25000,
processingDays: 3
},
'bill_payment': {
minAmount: 1,
maxAmount: 10000,
allowScheduled: true
}
}),
temporal: (context) => ({
canBackdate: false,
maxFutureDate: addDays(new Date(), 365),
excludeWeekends: context.transactionType === 'wire_transfer',
excludeHolidays: true
})
};
Legal: Document Filing
const filingConstraints = {
actor: (user) => ({
canFile: ['attorney', 'paralegal', 'clerk'].includes(user.role),
canWithdraw: user.role === 'attorney',
canAmend: user.barNumber ? true : false,
editWindow: {
'attorney': 24, // hours
'paralegal': 2,
'clerk': 0 // Cannot edit after filing
}[user.role]
}),
purpose: (filingType) => ({
'complaint': {
required: ['jurisdiction', 'parties', 'claims'],
maxPageLength: null,
allowsAttachments: true,
filingFee: 400
},
'motion': {
required: ['jurisdiction', 'case_number', 'relief_sought'],
maxPageLength: 25,
requiresBrief: true,
filingFee: 0
},
'discovery': {
required: ['case_number', 'discovery_type'],
maxPageLength: null,
deadlineRelative: 'trial_date',
daysBefore: 30
}
}),
temporal: (context) => ({
mustBeWithinStatute: context.filingType === 'complaint',
statuteDays: context.jurisdiction === 'federal' ? 90 : 120,
businessDaysOnly: true,
courtHoursOnly: { start: '08:00', end: '16:00' },
excludeCourtHolidays: true
})
};
E-commerce: Shipping Address
const shippingConstraints = {
actor: (user) => ({
canShipInternational: user.accountType !== 'basic',
expeditedAvailable: user.accountType === 'premium',
freeShippingThreshold: {
'basic': 50,
'plus': 35,
'premium': 0
}[user.accountType]
}),
purpose: (orderContext) => ({
required: orderContext.hasPhysicalItems,
verifyWithCarrier: orderContext.orderValue > 1000,
requireSignature: orderContext.orderValue > 5000 || orderContext.containsRegulated,
allowPOBox: !orderContext.requiresSignature && !orderContext.containsPerishable
}),
situational: (context) => ({
domesticOnly: context.itemCategory === 'hazmat',
restrictions: context.state === 'HI' || context.state === 'AK' ? ['oversized', 'perishable'] : [],
deliveryDays: {
'standard': context.inState ? 3 : 7,
'expedited': context.inState ? 1 : 3,
'international': 10
}
})
};
Education: Course Registration
const registrationConstraints = {
actor: (student) => ({
canRegister: student.accountStatus === 'active' && student.balanceDue === 0,
maxCredits: {
'full_time': 18,
'part_time': 9,
'graduate': 12
}[student.enrollment],
earlyRegistration: student.gpa >= 3.5 || student.seniority === 'senior',
waitlistPriority: student.major === 'course_department'
}),
purpose: (courseContext) => ({
prerequisitesMet: courseContext.prerequisites.every(p =>
student.completedCourses.includes(p)
),
corequisitesConcurrent: courseContext.corequisites.every(c =>
student.currentCourses.includes(c) || student.selectedCourses.includes(c)
),
majorRestriction: courseContext.restrictedToMajors ?
courseContext.allowedMajors.includes(student.major) : true
}),
temporal: (context) => ({
registrationWindow: {
start: context.earlyRegistration ?
new Date('2025-01-15') : new Date('2025-01-22'),
end: new Date('2025-02-01')
},
dropDeadline: new Date('2025-02-15'),
withdrawDeadline: new Date('2025-04-15'),
canAddAfterStart: context.courseStarted ? 7 : null // days
})
};
Related Patterns
Prerequisites: - Volume 3, Pattern 6: Domain-Aware Validation (provides validation logic) - Volume 3, Pattern 7: Calculated Dependencies (calculated values affect constraints)
Synergies: - Volume 3, Pattern 8: Intelligent Defaults (defaults respect constraints) - Volume 3, Pattern 10: Semantic Suggestions (suggest values that meet constraints) - Volume 3, Pattern 14: Cross-Field Validation (constraints often span fields)
Conflicts: - Overly complex rules (when to simplify vs add context) - User confusion (same field, different rules)
Alternatives: - Multiple separate forms (instead of one adaptive form) - Explicit mode selection (user chooses context explicitly) - Progressive constraints (relax over time)
Known Uses
Salesforce: Field-level security and validation rules vary by profile and record type
Healthcare Systems (Epic, Cerner): Appointment scheduling constraints based on provider, type, and insurance
Banking Apps: Transaction limits vary by account type, verification status, and transaction type
Travel Booking (Expedia, Kayak): Date constraints based on travel type (one-way, round-trip, multi-city)
HR Systems (Workday, BambooHR): PTO request constraints based on role, tenure, and accrual balance
E-commerce (Shopify, WooCommerce): Shipping options based on location, cart value, and product type
Government Forms (IRS, USCIS): Eligibility and requirements vary by applicant status and form purpose
Further Reading
Academic Foundations
- Context-Aware Computing: Dey, A.K. (2001). "Understanding and Using Context." Personal and Ubiquitous Computing 5(1): 4-7. https://link.springer.com/article/10.1007/s007790170019
- Adaptive Interfaces: Brusilovsky, P. (2001). "Adaptive Hypermedia." User Modeling and User-Adapted Interaction 11: 87-110. https://link.springer.com/article/10.1023/A:1011143116306
- Business Rules: Ross, R.G. (2003). Principles of the Business Rule Approach. Addison-Wesley. ISBN: 978-0201788938
Practical Implementation
- JSON Schema Conditional Logic: https://json-schema.org/understanding-json-schema/reference/conditionals.html - if/then/else conditionals
- React Hook Form Conditional Validation: https://react-hook-form.com/advanced-usage#ConditionalValidation - Dynamic validation based on context
- Formik Dependent Fields: https://formik.org/docs/examples/dependent-fields - Field dependency patterns
- Yup Conditional Schema: https://github.com/jquense/yup#yupwhenkeys-string--arraystring-builder-object--value-schema-schema-schema - Context-based validation
Standards & Specifications
- XForms Relevance Model: https://www.w3.org/TR/xforms11/#structure-relevant - W3C standard for context-dependent form controls
- BPMN Conditional Flows: https://www.bpmn.org/ - Business Process Model and Notation for context-driven workflows
- HL7 FHIR Questionnaire Logic: https://www.hl7.org/fhir/questionnaire.html#logic - Healthcare form context rules
Related Trilogy Patterns
- Pattern 6: Domain-Aware Validation - Constraints are domain-specific validation
- Pattern 7: Calculated Dependencies - Context drives calculations
- Pattern 13: Conditional Requirements - Requirements based on context
- Volume 2, Pattern 1: Universal Event Log - Tracking constraint violations
- Volume 2, Pattern 26: Feedback Loop Implementation - Learning from constraint patterns
- Volume 1, Chapter 3: Document Ontology - A Formal Framework - Modeling constraints formally
- Volume 1, Chapter 8: Architecture of Domain-Specific Systems - Rule evaluation architecture
Tools & Libraries
- json-rules-engine: https://github.com/CacheControl/json-rules-engine - Declarative rule engine for JavaScript
- nools: https://github.com/noolsjs/nools - Rete algorithm rules engine
- Drools: https://www.drools.org/ - Enterprise business rules management (Java)
- RuleBook: https://github.com/deliveredtechnologies/rulebook - Lightweight Java rules framework
Implementation Examples
- Progressive Disclosure Pattern: https://www.nngroup.com/articles/progressive-disclosure/ - Nielsen Norman Group article
- Adaptive Forms Tutorial: https://experienceleague.adobe.com/docs/experience-manager-65/forms/adaptive-forms-advanced-authoring/adaptive-forms-introduction.html - Adobe AEM approach
- Dynamic Forms Best Practices: https://uxplanet.org/designing-more-efficient-forms-structure-inputs-labels-and-actions-e3a47007114f - UX perspective