如何在NestJS中遵循单一职责原则(SRP)处理多服务协作
NestJS跨模块任务编排的合规实现方案
针对你遇到的场景,既不想在OrganizationService中注入其他服务破坏单一职责,也不想让控制器承担过多业务逻辑,这里提供两种合规的实现方案:
方案一:事件驱动模式(Event-Driven)
通过NestJS的@nestjs/event-emitter模块实现解耦,每个模块只关注自身的事件处理,核心服务仅负责发布事件,不直接调用其他服务。
步骤实现:
- 定义事件类:传递跨模块所需的必要数据
// src/organization/events/organization-created.event.ts export class OrganizationCreatedEvent { constructor( public readonly organizationId: string, public readonly userData: { email: string; name: string } ) {} }
- 核心服务发布事件:
OrganizationService只完成创建组织的核心逻辑,然后发布事件
// src/organization/organization.service.ts import { Injectable } from '@nestjs/common'; import { EventEmitter2 } from '@nestjs/event-emitter'; import { OrganizationCreatedEvent } from './events/organization-created.event'; @Injectable() export class OrganizationService { constructor( private readonly organizationRepository: OrganizationRepository, private readonly eventEmitter: EventEmitter2 ) {} async createOrganization(orgData: any) { // 仅处理创建组织的核心逻辑 const organization = await this.organizationRepository.create(orgData); // 发布事件,传递后续流程所需数据 this.eventEmitter.emit('organization.created', new OrganizationCreatedEvent( organization.id, orgData.userData )); return organization; } }
- 其他模块订阅事件:每个业务模块订阅对应事件,处理自身职责内的任务
// src/user/user.service.ts(创建用户) import { Injectable } from '@nestjs/common'; import { OnEvent } from '@nestjs/event-emitter'; import { OrganizationCreatedEvent } from '../organization/events/organization-created.event'; @Injectable() export class UserService { constructor( private readonly userRepository: UserRepository, private readonly eventEmitter: EventEmitter2 ) {} @OnEvent('organization.created') async handleOrganizationCreated(event: OrganizationCreatedEvent) { const user = await this.userRepository.create(event.userData); // 发布用户创建完成事件,供关联模块使用 this.eventEmitter.emit('user.created', { userId: user.id, organizationId: event.organizationId }); } }
// src/org-user-link/org-user-link.service.ts(关联用户与组织) import { Injectable } from '@nestjs/common'; import { OnEvent } from '@nestjs/event-emitter'; @Injectable() export class OrgUserLinkService { constructor( private readonly orgUserLinkRepository: OrgUserLinkRepository, private readonly eventEmitter: EventEmitter2 ) {} @OnEvent('user.created') async handleUserCreated(event: { userId: string; organizationId: string }) { await this.orgUserLinkRepository.create({ userId: event.userId, organizationId: event.organizationId }); // 发布关联完成事件,供邮件模块使用 this.eventEmitter.emit('org.user.linked', event); } }
// src/email/email.service.ts(发送邮件) import { Injectable } from '@nestjs/common'; import { OnEvent } from '@nestjs/event-emitter'; @Injectable() export class EmailService { constructor(private readonly userRepository: UserRepository) {} @OnEvent('org.user.linked') async handleOrgUserLinked(event: { userId: string; organizationId: string }) { const user = await this.userRepository.findOne({ where: { id: event.userId } }); await this.sendWelcomeEmail(user.email, event.organizationId); } private async sendWelcomeEmail(email: string, orgId: string) { // 邮件发送逻辑 } }
方案优势:
- 完全解耦各业务模块,每个服务仅负责自身核心逻辑,严格遵循单一职责原则
- 支持异步处理,某一步失败(如邮件发送失败)不会阻塞前面的业务流程
- 扩展性强,后续新增任务只需添加新的事件订阅者,无需修改核心服务
方案二:编排服务(Orchestration Service)
创建专门的编排服务作为中间层,集中管理业务流程的顺序调度,但不处理具体业务逻辑,各业务模块依然保持独立性。
步骤实现:
- 创建编排服务:注入所有需要的业务服务,负责协调流程
// src/organization-setup/organization-setup.service.ts import { Injectable } from '@nestjs/common'; import { OrganizationService } from '../organization/organization.service'; import { UserService } from '../user/user.service'; import { OrgUserLinkService } from '../org-user-link/org-user-link.service'; import { EmailService } from '../email/email.service'; @Injectable() export class OrganizationSetupService { constructor( private readonly orgService: OrganizationService, private readonly userService: UserService, private readonly linkService: OrgUserLinkService, private readonly emailService: EmailService ) {} async setupOrganization(setupData: any) { // 按顺序调度各业务服务 const organization = await this.orgService.createOrganization(setupData.orgData); const user = await this.userService.createUser(setupData.userData); await this.linkService.linkUserToOrg(user.id, organization.id); await this.emailService.sendWelcomeEmail(user.email, organization.id); return { organization, user }; } }
- 控制器调用编排服务:控制器仅负责接收请求,调用编排服务完成流程
// src/organization/organization.controller.ts import { Controller, Post, Body } from '@nestjs/common'; import { OrganizationSetupService } from '../organization-setup/organization-setup.service'; @Controller('organization') export class OrganizationController { constructor(private readonly setupService: OrganizationSetupService) {} @Post() async create(@Body() setupData: any) { return this.setupService.setupOrganization(setupData); } }
方案优势:
- 流程逻辑集中管理,步骤顺序清晰直观
- 各业务服务依然保持单一职责,编排服务仅做调度,不涉及具体业务
- 便于实现同步流程的失败回滚(如结合事务管理某一步失败时撤销前面的操作)
方案选择建议:
- 如果业务流程允许异步处理、容错要求高,优先选择事件驱动模式
- 如果需要严格保证流程顺序、同步执行或需要统一处理失败回滚,选择编排服务模式
内容的提问来源于stack exchange,提问作者Muhammadd Ayaz
相关产品推荐
相关产品推荐

