CourseKitBackend
ConflictService
Constraint pipeline for detecting scheduling conflicts
Overview
ConflictService runs all registered constraints against materialized schedule occurrences to detect conflicts. It supports both live checks and dry-run previews.
import { ConflictService } from '@hfu.digital/coursekit-nestjs';Methods
check
Check a single event against all registered constraints within a date range.
async check(event: TimetableEvent, dateRange: DateRange): Promise<ConflictCheckResult>| Parameter | Type | Description |
|---|---|---|
event | TimetableEvent | The event to check |
dateRange | DateRange | The date range to evaluate |
How it works:
- Fetches all events in the date range from storage
- Materializes all events (expanding RRULE + exceptions) into occurrences
- Builds a
ConstraintContextwith lookup helpers - Evaluates every registered
ScheduleConstraint - Collects all
Conflictresults - Emits
DOMAIN_EVENTS.CONFLICT_DETECTEDif any conflicts are found
const result = await conflicts.check(event, {
start: new Date('2026-03-01'),
end: new Date('2026-03-31'),
});
if (result.hasErrors) {
console.log('Blocking conflicts:', result.conflicts.filter(c => c.severity === 'error'));
}dryRun
Preview what conflicts a proposed event would create without persisting it.
async dryRun(
proposedEvent: Omit<TimetableEvent, 'id' | 'createdAt' | 'updatedAt' | 'version'>,
dateRange: DateRange,
): Promise<ConflictCheckResult>| Parameter | Type | Description |
|---|---|---|
proposedEvent | partial TimetableEvent | The proposed event data |
dateRange | DateRange | The date range to evaluate |
The proposed event is assigned a temporary ID and added to the existing schedule for evaluation. No data is written to storage and no domain events are emitted.
const result = await conflicts.dryRun(
{
title: 'Math 101',
startTime: new Date('2026-03-02T09:00:00'),
durationMin: 90,
recurrenceRule: 'FREQ=WEEKLY;BYDAY=MO',
roomId: 'room-a1',
courseId: null,
periodId: null,
metadata: null,
},
{ start: new Date('2026-03-01'), end: new Date('2026-06-30') },
);ConflictCheckResult
interface ConflictCheckResult {
hasErrors: boolean; // true if any conflict has severity 'error'
hasWarnings: boolean; // true if any conflict has severity 'warning'
conflicts: Conflict[]; // all detected violations
}Constraint Pipeline Flow
MaterializedOccurrence[]
│
├── OverlapConstraint.evaluate() → Conflict[] (instructor/room overlaps)
├── CapacityConstraint.evaluate() → Conflict[] (room over-capacity)
├── AvailabilityConstraint.evaluate() → Conflict[] (blocked time windows)
└── ...custom constraints...
│
▼
ConflictCheckResult { hasErrors, hasWarnings, conflicts }