feat: M0-02 EventBus — sync+async publish + DomainEvents queue
All checks were successful
Deploy API Server / build-and-deploy (push) Successful in 1m14s
All checks were successful
Deploy API Server / build-and-deploy (push) Successful in 1m14s
This commit is contained in:
parent
3fd5f94db5
commit
42e9e80f4c
20
package-lock.json
generated
20
package-lock.json
generated
@ -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",
|
||||||
|
|||||||
@ -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",
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
17
src/common/event-bus/event-bus.module.ts
Normal file
17
src/common/event-bus/event-bus.module.ts
Normal 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 {}
|
||||||
36
src/common/event-bus/event-bus.service.ts
Normal file
36
src/common/event-bus/event-bus.service.ts
Normal 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 || '';
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user