Event Bus
Ever Gauzy uses NestJS CQRS events for inter-module communication, enabling decoupled, event-driven architecture across the platform.
Architectureโ
The event bus allows modules to communicate without direct dependencies:
Core Conceptsโ
Eventsโ
Events represent something that has happened in the system:
// Event definition
export class EmployeeCreatedEvent implements IEvent {
constructor(
public readonly employee: IEmployee,
public readonly tenantId: string,
) {}
}
Event Handlersโ
Event handlers react to published events:
@EventsHandler(EmployeeCreatedEvent)
export class EmployeeCreatedHandler implements IEventHandler<EmployeeCreatedEvent> {
constructor(
private readonly emailService: EmailService,
private readonly activityLogService: ActivityLogService,
) {}
async handle(event: EmployeeCreatedEvent): Promise<void> {
const { employee, tenantId } = event;
// Send welcome email
await this.emailService.sendWelcomeEmail(employee);
// Log activity
await this.activityLogService.logActivity({
entity: "Employee",
entityId: employee.id,
action: "CREATE",
tenantId,
});
}
}
Publishing Eventsโ
Events are published through the EventBus:
@Injectable()
export class EmployeeService {
constructor(private readonly eventBus: EventBus) {}
async createEmployee(input: IEmployeeCreateInput): Promise<IEmployee> {
const employee = await this.repository.save(input);
// Publish event for other modules to react
this.eventBus.publish(
new EmployeeCreatedEvent(employee, RequestContext.currentTenantId()),
);
return employee;
}
}
Common Eventsโ
Entity Lifecycle Eventsโ
| Event | Published When |
|---|---|
EmployeeCreatedEvent | New employee record created |
EmployeeUpdatedEvent | Employee data modified |
OrganizationCreatedEvent | New organization created |
UserRegisteredEvent | New user registered |
InviteAcceptedEvent | User accepts an invitation |
Time Tracking Eventsโ
| Event | Published When |
|---|---|
TimeLogCreatedEvent | Time log entry recorded |
TimeLogUpdatedEvent | Time log modified |
TimeLogDeletedEvent | Time log removed |
TimesheetSubmittedEvent | Timesheet submitted for approval |
TimesheetApprovedEvent | Timesheet approved |
Integration Eventsโ
| Event | Published When |
|---|---|
IntegrationSyncEvent | Integration data synced |
GitHubWebhookEvent | GitHub webhook received |
UpworkSyncEvent | Upwork data synchronized |
Event Handler Registrationโ
Event handlers are registered in their respective modules:
@Module({
providers: [
// Register event handlers
EmployeeCreatedHandler,
EmployeeUpdatedHandler,
],
})
export class EmployeeModule {}
Multiple handlers can listen to the same event:
// In Notification Module
@EventsHandler(EmployeeCreatedEvent)
export class SendWelcomeNotification implements IEventHandler<EmployeeCreatedEvent> {
async handle(event: EmployeeCreatedEvent) {
// Send notification
}
}
// In Analytics Module
@EventsHandler(EmployeeCreatedEvent)
export class TrackEmployeeCreation implements IEventHandler<EmployeeCreatedEvent> {
async handle(event: EmployeeCreatedEvent) {
// Track analytics
}
}
Activity Log Integrationโ
The event bus powers the Activity Log system, which records all significant actions:
@EventsHandler(EmployeeCreatedEvent, TaskCreatedEvent, InvoiceCreatedEvent)
export class ActivityLogHandler implements IEventHandler {
constructor(private readonly activityLogService: ActivityLogService) {}
async handle(event: any) {
await this.activityLogService.log({
entityType: event.constructor.name,
entityId: event.entity?.id,
action: "CREATE",
description: `${event.constructor.name} triggered`,
actorId: RequestContext.currentUserId(),
tenantId: RequestContext.currentTenantId(),
});
}
}
Best Practicesโ
DOโ
- โ Use events for cross-module communication
- โ Keep event handlers idempotent
- โ
Include
tenantIdin all events for multi-tenant support - โ Handle errors gracefully in event handlers (don't break the publisher)
- โ Use descriptive event names
DON'Tโ
- โ Use events for synchronous request-response patterns (use Commands/Queries instead)
- โ Throw exceptions in event handlers that should propagate to the caller
- โ Depend on event handler execution order
- โ Store large payloads in events (reference IDs instead)
Related Pagesโ
- Backend Architecture โ NestJS CQRS patterns
- Plugin System โ plugins and events