feat: M0-02 EventBus — sync+async publish + DomainEvents queue
All checks were successful
Deploy API Server / build-and-deploy (push) Successful in 1m14s

This commit is contained in:
WangDL 2026-05-22 22:22:25 +08:00
parent 3fd5f94db5
commit 42e9e80f4c
5 changed files with 76 additions and 0 deletions

20
package-lock.json generated
View File

@ -14,6 +14,7 @@
"@nestjs/common": "^11.0.1", "@nestjs/common": "^11.0.1",
"@nestjs/config": "^4.0.4", "@nestjs/config": "^4.0.4",
"@nestjs/core": "^11.0.1", "@nestjs/core": "^11.0.1",
"@nestjs/event-emitter": "^3.1.0",
"@nestjs/jwt": "^11.0.2", "@nestjs/jwt": "^11.0.2",
"@nestjs/passport": "^11.0.5", "@nestjs/passport": "^11.0.5",
"@nestjs/platform-express": "^11.0.1", "@nestjs/platform-express": "^11.0.1",
@ -2555,6 +2556,19 @@
} }
} }
}, },
"node_modules/@nestjs/event-emitter": {
"version": "3.1.0",
"resolved": "https://registry.npmmirror.com/@nestjs/event-emitter/-/event-emitter-3.1.0.tgz",
"integrity": "sha512-DOY/4XBGyIjYyOJKkO6jl1kzFE0ZfX0wV+M2HR5NWymPT9Z0zdCEcZGxTXXkoMRwPtglnvCGJALSjOpXPIcM3g==",
"license": "MIT",
"dependencies": {
"eventemitter2": "6.4.9"
},
"peerDependencies": {
"@nestjs/common": "^10.0.0 || ^11.0.0",
"@nestjs/core": "^10.0.0 || ^11.0.0"
}
},
"node_modules/@nestjs/jwt": { "node_modules/@nestjs/jwt": {
"version": "11.0.2", "version": "11.0.2",
"resolved": "https://registry.npmmirror.com/@nestjs/jwt/-/jwt-11.0.2.tgz", "resolved": "https://registry.npmmirror.com/@nestjs/jwt/-/jwt-11.0.2.tgz",
@ -5972,6 +5986,12 @@
"node": ">= 0.6" "node": ">= 0.6"
} }
}, },
"node_modules/eventemitter2": {
"version": "6.4.9",
"resolved": "https://registry.npmmirror.com/eventemitter2/-/eventemitter2-6.4.9.tgz",
"integrity": "sha512-JEPTiaOt9f04oa6NOkc4aH+nVp5I3wEjpHbIPqfgCdD5v5bUzy7xQqwcVO2aDQgOWhI28da57HksMrzK9HlRxg==",
"license": "MIT"
},
"node_modules/events": { "node_modules/events": {
"version": "3.3.0", "version": "3.3.0",
"resolved": "https://registry.npmmirror.com/events/-/events-3.3.0.tgz", "resolved": "https://registry.npmmirror.com/events/-/events-3.3.0.tgz",

View File

@ -29,6 +29,7 @@
"@nestjs/common": "^11.0.1", "@nestjs/common": "^11.0.1",
"@nestjs/config": "^4.0.4", "@nestjs/config": "^4.0.4",
"@nestjs/core": "^11.0.1", "@nestjs/core": "^11.0.1",
"@nestjs/event-emitter": "^3.1.0",
"@nestjs/jwt": "^11.0.2", "@nestjs/jwt": "^11.0.2",
"@nestjs/passport": "^11.0.5", "@nestjs/passport": "^11.0.5",
"@nestjs/platform-express": "^11.0.1", "@nestjs/platform-express": "^11.0.1",

View File

@ -6,6 +6,7 @@ import { JwtModule } from '@nestjs/jwt';
import { PrismaModule } from './infrastructure/database/prisma.module'; import { PrismaModule } from './infrastructure/database/prisma.module';
import { RedisModule } from './infrastructure/redis/redis.module'; import { RedisModule } from './infrastructure/redis/redis.module';
import { QueueModule } from './infrastructure/queue/queue.module'; import { QueueModule } from './infrastructure/queue/queue.module';
import { EventBusModule } from './common/event-bus/event-bus.module';
import { AiModule } from './modules/ai/ai.module'; import { AiModule } from './modules/ai/ai.module';
import { StorageModule } from './infrastructure/storage/storage.module'; import { StorageModule } from './infrastructure/storage/storage.module';
import { LoggerModule } from './infrastructure/logger/logger.module'; import { LoggerModule } from './infrastructure/logger/logger.module';
@ -85,6 +86,7 @@ import appleConfig from './config/apple.config';
}), }),
PrismaModule, PrismaModule,
RedisModule, RedisModule,
EventBusModule,
QueueModule, QueueModule,
AiModule, AiModule,
StorageModule, StorageModule,

View File

@ -0,0 +1,17 @@
import { Global, Module } from '@nestjs/common';
import { EventEmitterModule } from '@nestjs/event-emitter';
import { BullModule } from '@nestjs/bullmq';
import { EventBusService } from './event-bus.service';
export const QUEUE_DOMAIN_EVENTS = 'domain-events';
@Global()
@Module({
imports: [
EventEmitterModule.forRoot({ wildcard: true, delimiter: '.' }),
BullModule.registerQueue({ name: QUEUE_DOMAIN_EVENTS }),
],
providers: [EventBusService],
exports: [EventBusService, EventEmitterModule],
})
export class EventBusModule {}

View File

@ -0,0 +1,36 @@
import { Injectable, Logger } from '@nestjs/common';
import { EventEmitter2 } from '@nestjs/event-emitter';
import { QueueService } from '../../infrastructure/queue/queue.service';
import { BaseDomainEvent } from '../events/base-domain.event';
import { safeLog } from '../../infrastructure/logger/sensitive-logger';
export const DOMAIN_EVENT = 'domain.event';
@Injectable()
export class EventBusService {
private readonly logger = new Logger(EventBusService.name);
constructor(
private readonly eventEmitter: EventEmitter2,
private readonly queue: QueueService,
) {}
/** Sync: process-in-memory, low latency, fire-and-forget */
publish(event: BaseDomainEvent): void {
this.logger.log(`[sync] ${event.eventType} id=${event.eventId}`);
this.eventEmitter.emit(DOMAIN_EVENT, event);
this.eventEmitter.emit(event.eventType, event);
}
/** Async: persistent via BullMQ, retry + DLQ */
async publishAsync(event: BaseDomainEvent): Promise<string> {
const job = await this.queue.add('domain-events', {
eventType: event.eventType,
eventId: event.eventId,
payload: safeLog(event),
occurredAt: event.occurredAt.toISOString(),
});
this.logger.log(`[async] ${event.eventType} id=${event.eventId} job=${job.id}`);
return job.id || '';
}
}