You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

NestJS实现Clean Architecture遇分层依赖难题求指导

在NestJS中落地Clean Architecture:解决外部库依赖问题的实用方案

1. 拆分领域实体与ORM适配类

领域层只保留纯领域实体——不含任何TypeORM装饰器,只封装核心业务属性、行为和规则,比如:

// domain/entities/user.entity.ts
export class User {
  constructor(
    public readonly id: string,
    public readonly email: string,
    public readonly passwordHash: string
  ) {}

  // 业务方法示例:验证密码逻辑
  validatePassword(password: string): boolean {
    // 核心业务逻辑(可依赖应用层抽象服务,而非直接调用加密库)
  }
}

基础设施层创建TypeORM专用实体,作为领域实体的映射适配层,负责数据库交互:

// infrastructure/persistence/entities/user.entity.ts
import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';

@Entity()
export class UserEntity {
  @PrimaryGeneratedColumn('uuid')
  id: string;

  @Column({ unique: true })
  email: string;

  @Column()
  passwordHash: string;

  // 领域实体转ORM实体
  static fromDomain(user: User): UserEntity {
    const entity = new UserEntity();
    entity.id = user.id;
    entity.email = user.email;
    entity.passwordHash = user.passwordHash;
    return entity;
  }

  // ORM实体转领域实体
  toDomain(): User {
    return new User(this.id, this.email, this.passwordHash);
  }
}

用automapper统一管理映射逻辑,避免重复代码,同时保证领域层完全独立于TypeORM。

2. 分层处理DTO,隔离Swagger依赖

  • 应用层DTO:定义纯业务契约,只包含业务需要的字段和class-validator验证规则,不涉及Swagger装饰器:
// application/dtos/create-user.dto.ts
import { IsEmail, IsString, MinLength } from 'class-validator';

export class CreateUserDto {
  @IsEmail()
  email: string;

  @IsString()
  @MinLength(8)
  password: string;
}
  • API层DTO:放在基础设施层或单独的api目录,继承应用层DTO并添加Swagger装饰器,负责API文档生成:
// infrastructure/api/dtos/create-user.dto.ts
import { ApiProperty } from '@nestjs/swagger';
import { CreateUserDto } from '../../../application/dtos/create-user.dto';

export class ApiCreateUserDto extends CreateUserDto {
  @ApiProperty({ example: 'user@example.com', description: '用户邮箱' })
  email: string;

  @ApiProperty({ example: 'StrongPass123', description: '用户密码,至少8位' })
  password: string;
}

控制器接收ApiCreateUserDto后,转换为应用层的CreateUserDto传递给应用服务,保证应用层不依赖Swagger。

3. 用依赖倒置隔离第三方服务(如JWT)

应用层定义抽象接口,基础设施层实现具体逻辑:

// application/interfaces/token-service.interface.ts
export interface TokenService {
  generateToken(userId: string): string;
  verifyToken(token: string): string;
}

基础设施层用JWT库实现这个接口:

// infrastructure/auth/jwt-token.service.ts
import { Injectable } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { TokenService } from '../../application/interfaces/token-service.interface';

@Injectable()
export class JwtTokenService implements TokenService {
  constructor(private readonly jwtService: JwtService) {}

  generateToken(userId: string): string {
    return this.jwtService.sign({ sub: userId });
  }

  verifyToken(token: string): string {
    const payload = this.jwtService.verify(token);
    return payload.sub;
  }
}

在NestJS模块中绑定接口与实现:

// auth/auth.module.ts
import { Module } from '@nestjs/common';
import { JwtModule } from '@nestjs/jwt';
import { TokenService } from '../application/interfaces/token-service.interface';
import { JwtTokenService } from './jwt-token.service';

@Module({
  imports: [JwtModule.register({ secret: process.env.JWT_SECRET })],
  providers: [{ provide: TokenService, useClass: JwtTokenService }],
  exports: [TokenService],
})
export class AuthModule {}

这样应用层只依赖抽象的TokenService,完全不关心JWT的具体实现。

4. 架构选择的权衡

Clean Architecture的核心是让业务逻辑独立于外部依赖,保证领域层可测试、可演进,而非死守目录结构教条。

  • 如果是个人练手项目,建议坚持落地上述方案,能帮你深刻理解依赖倒置和分层思想;
  • 如果项目规模极小、快速交付优先级更高,也可以采用NestJS常规模块化结构,但尽量保留核心业务逻辑的独立性,避免将业务代码与ORM/API代码混写。

内容的提问来源于stack exchange,提问作者Aldemar Cuartas Carvajal

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 02:32:34