Skip to main content

Events Module

The Events Module (@ever-works/agent/events) defines the domain event classes used for cross-module communication within the agent package. Events follow the NestJS @nestjs/event-emitter pattern with a typed BaseEvent abstract class.

Module Structure​

packages/agent/src/events/
├── index.ts # Barrel exports
├── base.ts # BaseEvent abstract class
├── work-created.event.ts # WorkCreatedEvent
└── work-generation-completed.event.ts # WorkGenerationCompletedEvent

Architecture​

Events decouple producers from consumers. A service emits an event without knowing which handlers will process it. Multiple handlers can subscribe to the same event.

Key Components​

BaseEvent​

The abstract base class that all domain events extend:

export abstract class BaseEvent {
static EVENT_NAME: string;
}

Every concrete event class must define a static EVENT_NAME string that identifies the event type. This string is used with EventEmitter2.emit() and the @OnEvent() decorator.

WorkCreatedEvent​

Emitted when a new work is created.

export class WorkCreatedEvent extends BaseEvent {
static EVENT_NAME = 'work.created';

constructor(
public readonly workId: string,
public readonly userId: string,
public readonly slug: string
) {
super();
}
}
PropertyTypeDescription
workIdstringUUID of the newly created work
userIdstringUUID of the user who created it
slugstringURL slug of the work

Typical subscribers: Notification services, analytics, default setup workflows.

WorkGenerationCompletedEvent​

Emitted when a work generation run finishes (successfully or with an error).

export class WorkGenerationCompletedEvent extends BaseEvent {
static EVENT_NAME = 'work.generation.completed';

constructor(
public readonly workId: string,
public readonly userId: string,
public readonly success: boolean,
public readonly error?: string
) {
super();
}
}
PropertyTypeDescription
workIdstringUUID of the work
userIdstringUUID of the user who triggered generation
successbooleanWhether generation completed without errors
errorstring (optional)Error message if success is false

Typical subscribers: Notification services, history recording, schedule advancement, deployment triggers.

Usage​

Emitting Events​

import { EventEmitter2 } from '@nestjs/event-emitter';
import { WorkCreatedEvent } from '@ever-works/agent/events';

@Injectable()
export class WorkService {
constructor(private readonly eventEmitter: EventEmitter2) {}

async createWork(dto: CreateWorkDto, userId: string) {
const work = await this.save(dto);

this.eventEmitter.emit(WorkCreatedEvent.EVENT_NAME, new WorkCreatedEvent(work.id, userId, work.slug));

return work;
}
}

Handling Events​

import { OnEvent } from '@nestjs/event-emitter';
import { WorkGenerationCompletedEvent } from '@ever-works/agent/events';

@Injectable()
export class NotificationHandler {
@OnEvent(WorkGenerationCompletedEvent.EVENT_NAME)
async handleGenerationCompleted(event: WorkGenerationCompletedEvent) {
if (!event.success) {
await this.notifyUser(event.userId, `Generation failed: ${event.error}`);
}
}
}

Design Conventions​

  1. Static EVENT_NAME: Each event class has a static string property following the pattern domain.action (e.g., work.created). This enables type-safe event registration.

  2. Immutable payloads: All event properties are readonly to prevent handlers from mutating shared state.

  3. Flat hierarchy: Events extend BaseEvent directly rather than forming deep inheritance chains. Each event is self-contained with all necessary context.

  4. Naming convention: Event files are named {subject}-{action}.event.ts and classes are named {Subject}{Action}Event.