NestJS模块依赖注入跨模块绑定位置咨询
NestJS跨模块DI绑定方案
问题1:领域模块定义接口、基础设施模块实现,避免模块耦合
核心思路是在顶层聚合模块(如UserModule)中完成接口与实现的绑定,让领域模块只依赖抽象,基础设施模块提供实现,二者通过顶层模块关联,无需互相导入。
- 步骤1:在UserDomainModule中定义接口和DI Token(用Symbol)
// user.domain.ts export const USER_REPOSITORY = Symbol('USER_REPOSITORY'); export interface UserRepository { findById(id: string): Promise<User>; } // user-domain.module.ts @Module({ providers: [], exports: [USER_REPOSITORY] // 导出Token,供上层模块绑定实现 }) export class UserDomainModule {}
- 步骤2:在UserInfrastructureModule中实现接口
// user.infrastructure.ts @Injectable() export class UserRepositoryImpl implements UserRepository { async findById(id: string): Promise<User> { // 具体数据库操作逻辑 } } // user-infrastructure.module.ts @Module({ providers: [UserRepositoryImpl], exports: [UserRepositoryImpl] // 导出实现类 }) export class UserInfrastructureModule {}
- 步骤3:在顶层UserModule中完成绑定,同时导入两个子模块并导出绑定后的Provider
// user.module.ts @Module({ imports: [UserDomainModule, UserInfrastructureModule], providers: [ { provide: USER_REPOSITORY, useClass: UserRepositoryImpl // 关联Token与实现 } ], exports: [USER_REPOSITORY] // 导出绑定后的Provider,供其他模块使用 }) export class UserModule {}
后续在UserDomainModule的服务中,直接通过@Inject(USER_REPOSITORY)注入即可,无需导入基础设施模块。
问题2:Card模块定义接口、User模块实现,避免直接导入UserDomainModule
采用端口-适配器模式,Card侧定义抽象端口,User侧实现适配器,在User模块中完成绑定,Card模块仅依赖抽象即可获取实现。
- 步骤1:在CardModule中定义接口和DI Token
// card.port.ts export const USER_CLIENT = Symbol('USER_CLIENT'); export interface UserClient { getUserInfo(userId: string): Promise<UserInfo>; } // card.module.ts @Module({ providers: [] }) export class CardModule {}
- 步骤2:在UserModule中实现Card定义的接口
// user.client.ts @Injectable() export class UserClientImpl implements UserClient { async getUserInfo(userId: string): Promise<UserInfo> { // 调用User领域的服务逻辑 } }
- 步骤3:在UserModule中绑定Card的Token与实现,并导出该Provider
// user.module.ts @Module({ imports: [UserDomainModule], providers: [ UserClientImpl, { provide: USER_CLIENT, useClass: UserClientImpl // 关联Card侧Token与User侧实现 } ], exports: [USER_CLIENT] // 导出绑定后的Provider }) export class UserModule {}
- 步骤4:在CardModule中导入UserModule,在服务中注入使用
// card.service.ts @Injectable() export class CardService { constructor(@Inject(USER_CLIENT) private readonly userClient: UserClient) {} async getCardWithUserInfo(cardId: string) { const card = await this.cardRepository.findById(cardId); const userInfo = await this.userClient.getUserInfo(card.userId); return { ...card, userInfo }; } } // card.module.ts @Module({ imports: [UserModule], // 仅导入UserModule,而非整个UserDomainModule providers: [CardService, CardRepository], controllers: [CardController] }) export class CardModule {}
若要进一步降低耦合,可将Card侧的接口抽为独立的公共模块,让User和Card模块都依赖该模块,避免User模块直接引用Card侧代码。
内容的提问来源于stack exchange,提问作者Pyrkosz
相关产品推荐
相关产品推荐

