fix: break circular dependency between Events↔Sisters↔Activity

- Replace EventsGateway direct dependency in ActivityService with EventEmitter2
- ActivityModule no longer imports EventsModule (cycle broken)
- Register EventEmitterModule in AppModule
- Update activity.service.spec.ts to provide EventEmitter2
- SistersModule safely imports ActivityModule without creating a cycle

Dependency graph after fix:
  EventsModule → SistersModule → ActivityModule (leaf, no back-edge)
This commit is contained in:
2026-04-08 14:07:19 +09:00
parent 88547e9464
commit d8f2818e59
6 changed files with 30 additions and 8 deletions

View File

@@ -12,6 +12,7 @@
"@nestjs/common": "^11.0.1",
"@nestjs/config": "^4.0.3",
"@nestjs/core": "^11.0.1",
"@nestjs/event-emitter": "^3.0.1",
"@nestjs/jwt": "^11.0.2",
"@nestjs/passport": "^11.0.5",
"@nestjs/platform-express": "^11.0.1",
@@ -2294,6 +2295,19 @@
}
}
},
"node_modules/@nestjs/event-emitter": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/@nestjs/event-emitter/-/event-emitter-3.0.1.tgz",
"integrity": "sha512-0Ln/x+7xkU6AJFOcQI9tIhUMXVF7D5itiaQGOyJbXtlAfAIt8gzDdJm+Im7cFzKoWkiW5nCXCPh6GSvdQd/3Dw==",
"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": {
"version": "11.0.2",
"resolved": "https://registry.npmjs.org/@nestjs/jwt/-/jwt-11.0.2.tgz",
@@ -6204,6 +6218,12 @@
"node": ">= 0.6"
}
},
"node_modules/eventemitter2": {
"version": "6.4.9",
"resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.9.tgz",
"integrity": "sha512-JEPTiaOt9f04oa6NOkc4aH+nVp5I3wEjpHbIPqfgCdD5v5bUzy7xQqwcVO2aDQgOWhI28da57HksMrzK9HlRxg==",
"license": "MIT"
},
"node_modules/events": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",

View File

@@ -27,6 +27,7 @@
"@nestjs/common": "^11.0.1",
"@nestjs/config": "^4.0.3",
"@nestjs/core": "^11.0.1",
"@nestjs/event-emitter": "^3.0.1",
"@nestjs/jwt": "^11.0.2",
"@nestjs/passport": "^11.0.5",
"@nestjs/platform-express": "^11.0.1",

View File

@@ -2,10 +2,9 @@ import { Module } from '@nestjs/common';
import { ActivityController } from './activity.controller';
import { ActivityService } from './activity.service';
import { PrismaModule } from '../prisma/prisma.module';
import { EventsModule } from '../events/events.module';
@Module({
imports: [PrismaModule, EventsModule],
imports: [PrismaModule],
controllers: [ActivityController],
providers: [ActivityService],
exports: [ActivityService],

View File

@@ -1,4 +1,5 @@
import { Test, TestingModule } from '@nestjs/testing';
import { EventEmitter2 } from '@nestjs/event-emitter';
import { ActivityService } from './activity.service';
import { PrismaService } from '../prisma/prisma.service';
@@ -20,6 +21,7 @@ describe('ActivityService', () => {
providers: [
ActivityService,
{ provide: PrismaService, useValue: mockPrisma },
EventEmitter2,
],
}).compile();

View File

@@ -1,6 +1,6 @@
import { Injectable, Optional } from '@nestjs/common';
import { Injectable } from '@nestjs/common';
import { EventEmitter2 } from '@nestjs/event-emitter';
import { PrismaService } from '../prisma/prisma.service';
import { EventsGateway } from '../events/events.gateway';
export interface LogActivityDto {
sisterId?: number;
@@ -34,7 +34,7 @@ function sanitizeActivityDetail(detail?: string | null): string | undefined {
export class ActivityService {
constructor(
private readonly prisma: PrismaService,
@Optional() private readonly events?: EventsGateway,
private readonly eventEmitter: EventEmitter2,
) {}
async log(dto: LogActivityDto) {
@@ -49,9 +49,7 @@ export class ActivityService {
},
});
if (this.events) {
this.events.broadcastActivity(record);
}
this.eventEmitter.emit('activity.logged', record);
return record;
}

View File

@@ -1,5 +1,6 @@
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { EventEmitterModule } from '@nestjs/event-emitter';
import { ThrottlerModule } from '@nestjs/throttler';
import { AppController } from './app.controller';
import { AppService } from './app.service';
@@ -22,6 +23,7 @@ import { DashboardModule } from './dashboard/dashboard.module';
@Module({
imports: [
ConfigModule.forRoot({ isGlobal: true }),
EventEmitterModule.forRoot(),
ThrottlerModule.forRoot([{ ttl: 60000, limit: 100 }]),
PrismaModule,
SistersModule,