用于打字稿输入的 DTO、模式或接口的最佳实践是什么

Had*_*ock 0 typescript nestjs

目前,在我的打字稿代码(nestjs)中,我使用控制器中的 DTO 来验证进入 API 的数据,模式用作其余文件中的类型,并且除特殊情况外我不会创建接口。

我试图弄清楚我正在做的事情是否好,或者我是否应该在任何地方使用 DTO 作为类型,或者其他什么?目标是提高代码的质量。

用户集合示例:

user.schema.ts

import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import { Document } from 'mongoose';

@Schema({ collection: 'users' })
export class User extends Document {
  @Prop()
  name: string;
}

export const UserSchema = SchemaFactory.createForClass(User);
UserSchema.set('timestamps', true);
Run Code Online (Sandbox Code Playgroud)

user.dto.ts

import { IsNotEmpty, IsString, Length } from 'class-validator';
import { ApiProperty } from '@nestjs/swagger';

export class UserDto {
  @IsNotEmpty()
  @IsString()
  @Length(3)
  @ApiProperty({ required: true })
  readonly name: string;
}
Run Code Online (Sandbox Code Playgroud)

使用示例:

  • 在带有 dto 的控制器中
async createUser(@Body() user: UserDto): Promise<UserSchema> {
  const nameAlreadyExist = await this.userService.getUserByField('name', user.name);
  if (nameAlreadyExist && nameAlreadyExist.length > 0) {
    throw new ConflictException(getErrorObject(ErrorCodeEnum.duplicate, EntityCodeEnum.user, ['name']));
  }
  return this.userService.createUser(user as UserSchema);
}
Run Code Online (Sandbox Code Playgroud)
  • 在具有架构的服务中
async createUser(user: UserSchema): Promise<UserSchema> {
  const newUser = new this.UserModel(user);
  return newUser.save();
}
Run Code Online (Sandbox Code Playgroud)

Jes*_*ter 10

DTO 模式在 NestJS 上下文中非常有用,因为当您使用实际的类时,您可以应用装饰器来验证 API 边界处的请求。

模式是 Mongo 概念,需要使用模式才能从数据库读取和写入。如果 Schema 和 DTO 之间完全重叠,您可以考虑将它们合并到一个类中并组合它们的装饰器。然而,通常认为在 API 层处理模型的单独非数据库表示是一种好的做法。

接口是 TypeScript 语言的一项通用功能,能够描述数据的形状。当您想要在您控制的代码内部强制执行编译时安全时,它们在库代码中非常有用。一旦进入服务层,如果不需要装饰器,您始终可以使用接口或类型来促进类型安全。

你的问题没有单一的正确答案。它始终取决于您的应用程序的需求,但总而言之,我认为遵循的一个好的结构是:

  • API 边界的 DTO 用于验证来自其他系统的消息
  • 服务层中的架构/数据库模型可实现与控制器分开的数据库交互
  • 所有不需要装饰器的内部库代码的接口/类型