NestJS Event-Driven Pattern
Build event-driven systems with EventEmitter2, CQRS module, CommandBus, and QueryBus
When to Use
- You need to decouple side effects (send email, update audit log) from the primary operation
- You are implementing CQRS to separate read and write models for a bounded context
- You need event sourcing where domain events are the source of truth
- You want to publish domain events that multiple services can independently handle
Instructions
EventEmitter2 (simple pub/sub within the same process)
- Setup:
npm install @nestjs/event-emitter
@Module({ imports: [EventEmitterModule.forRoot()] })
export class AppModule {}
- Emit events from a service:
@Injectable()
export class UsersService {
constructor(private eventEmitter: EventEmitter2) {}
async create(dto: CreateUserDto): Promise<User> {
const user = await this.prisma.user.create({ data: dto });
this.eventEmitter.emit('user.created', new UserCreatedEvent(user));
return user;
}
}
- Handle events:
@Injectable()
export class EmailNotificationListener {
@OnEvent('user.created')
async handleUserCreated(event: UserCreatedEvent): Promise<void> {
await this.mailService.sendWelcomeEmail(event.user.email);
}
@OnEvent('user.*') // wildcard
async handleAnyUserEvent(event: unknown): Promise<void> { ... }
}
CQRS Module (commands and queries)
npm install @nestjs/cqrs
// command
export class CreateUserCommand { constructor(public dto: CreateUserDto) {} }
// handler
@CommandHandler(CreateUserCommand)
export class CreateUserHandler implements ICommandHandler<CreateUserCommand> {
constructor(private usersRepo: UsersRepository) {}
async execute(command: CreateUserCommand): Promise<User> {
return this.usersRepo.create(command.dto);
}
}
// dispatch from controller
@Post()
create(@Body() dto: CreateUserDto) {
return this.commandBus.execute(new CreateUserCommand(dto));
}
// query
@QueryHandler(GetUserQuery)
export class GetUserHandler implements IQueryHandler<GetUserQuery> {
async execute(query: GetUserQuery): Promise<User> {
return this.usersReadRepo.findById(query.id);
}
}
Register all handlers in providers.
Details
EventEmitter2 vs CQRS: EventEmitter2 is in-process pub/sub — simple, zero setup overhead, good for side effects. CQRS is a structural pattern that separates commands (writes) from queries (reads) — use it when you need independent scaling of read/write paths or when the read model differs significantly from the write model.
EventEmitter2 async handlers: @OnEvent('user.created', { async: true }) ensures async handlers do not block the event loop. Set promisify: true on EventEmitterModule.forRoot() to await async handlers before continuing.
EventBus (CQRS): In addition to CommandBus and QueryBus, the CQRS module provides EventBus for domain events. IEventHandler implementations react to events published via eventBus.publish(new UserCreatedEvent(user)).
Saga pattern: @nestjs/cqrs supports long-running processes via @Saga() decorator — these are RxJS streams that react to events and dispatch commands.
Cross-service events: EventEmitter2 is in-process only. For cross-service messaging (microservices), use the NestJS microservices transport layer (@EventPattern, Redis/RabbitMQ) or integrate with Kafka/NATS for durable event streams.
Source
https://docs.nestjs.com/recipes/cqrs
Process
- Read the instructions and examples in this document.
- Apply the patterns to your implementation, adapting to your specific context.
- Verify your implementation against the details and edge cases listed above.
Harness Integration
- Type: knowledge — this skill is a reference document, not a procedural workflow.
- No tools or state — consumed as context by other skills and agents.
Success Criteria
- The patterns described in this document are applied correctly in the implementation.
- Edge cases and anti-patterns listed in this document are avoided.
Expert Next.js App Router
Developpement
Un skill qui transforme Claude en expert Next.js App Router.
Générateur de README
Developpement
Crée des README.md professionnels et complets pour vos projets.
Rédacteur de Documentation API
Developpement
Génère de la documentation API complète au format OpenAPI/Swagger.