CourseKitBackend
Domain Events
Event-driven architecture with @nestjs/event-emitter integration
Overview
CourseKit emits domain events for all state-changing operations via @nestjs/event-emitter. Subscribe to these events to build notifications, audit logs, sync systems, or other reactive integrations.
import { DOMAIN_EVENTS } from '@hfu.digital/coursekit-nestjs';Event Constants
const DOMAIN_EVENTS = {
EVENT_CREATED: 'coursekit.event.created',
EVENT_UPDATED: 'coursekit.event.updated',
EVENT_DELETED: 'coursekit.event.deleted',
EXCEPTION_CREATED: 'coursekit.exception.created',
EXCEPTION_DELETED: 'coursekit.exception.deleted',
CONFLICT_DETECTED: 'coursekit.conflict.detected',
AVAILABILITY_CREATED: 'coursekit.availability.created',
AVAILABILITY_UPDATED: 'coursekit.availability.updated',
AVAILABILITY_DELETED: 'coursekit.availability.deleted',
} as const;Payload Types
EventCreatedPayload
interface EventCreatedPayload {
event: TimetableEvent;
}EventUpdatedPayload
interface EventUpdatedPayload {
previous: TimetableEvent;
current: TimetableEvent;
changedFields: string[];
}EventDeletedPayload
interface EventDeletedPayload {
event: TimetableEvent;
}ExceptionCreatedPayload
interface ExceptionCreatedPayload {
exception: EventException;
parentEvent: TimetableEvent;
}ExceptionDeletedPayload
interface ExceptionDeletedPayload {
exception: EventException;
parentEvent: TimetableEvent;
}ConflictDetectedPayload
interface ConflictDetectedPayload {
result: ConflictCheckResult;
triggeringEventId: string;
}AvailabilityCreatedPayload
interface AvailabilityCreatedPayload {
availability: Availability;
}AvailabilityUpdatedPayload
interface AvailabilityUpdatedPayload {
previous: Availability;
current: Availability;
}AvailabilityDeletedPayload
interface AvailabilityDeletedPayload {
availability: Availability;
}Subscribing to Events
Use the @OnEvent decorator from @nestjs/event-emitter:
import { Injectable } from '@nestjs/common';
import { OnEvent } from '@nestjs/event-emitter';
import {
DOMAIN_EVENTS,
type EventCreatedPayload,
type ConflictDetectedPayload,
type AvailabilityUpdatedPayload,
} from '@hfu.digital/coursekit-nestjs';
@Injectable()
export class ScheduleNotificationService {
@OnEvent(DOMAIN_EVENTS.EVENT_CREATED)
handleEventCreated(payload: EventCreatedPayload) {
console.log('New event scheduled:', payload.event.title);
}
@OnEvent(DOMAIN_EVENTS.CONFLICT_DETECTED)
handleConflict(payload: ConflictDetectedPayload) {
const { result, triggeringEventId } = payload;
console.warn(
`${result.conflicts.length} conflicts detected for event ${triggeringEventId}`,
);
}
@OnEvent(DOMAIN_EVENTS.AVAILABILITY_UPDATED)
handleAvailabilityChange(payload: AvailabilityUpdatedPayload) {
console.log(
`Availability updated from ${payload.previous.type} to ${payload.current.type}`,
);
}
}DomainEventMap
For fully typed event subscriptions, use the DomainEventMap interface:
interface DomainEventMap {
[DOMAIN_EVENTS.EVENT_CREATED]: EventCreatedPayload;
[DOMAIN_EVENTS.EVENT_UPDATED]: EventUpdatedPayload;
[DOMAIN_EVENTS.EVENT_DELETED]: EventDeletedPayload;
[DOMAIN_EVENTS.EXCEPTION_CREATED]: ExceptionCreatedPayload;
[DOMAIN_EVENTS.EXCEPTION_DELETED]: ExceptionDeletedPayload;
[DOMAIN_EVENTS.CONFLICT_DETECTED]: ConflictDetectedPayload;
[DOMAIN_EVENTS.AVAILABILITY_CREATED]: AvailabilityCreatedPayload;
[DOMAIN_EVENTS.AVAILABILITY_UPDATED]: AvailabilityUpdatedPayload;
[DOMAIN_EVENTS.AVAILABILITY_DELETED]: AvailabilityDeletedPayload;
}