在 JavaScript 和 Node.js 中正确使用 DTO 详解

作者:终端旅客

时间:2026-10-31

浏览:576

本文详解在 JavaScript 和 Node.js 开发中引入数据传输对象(DTO)的必要性。通过对比直接传递数据库模型与使用 DTO 的差异,展示了如何利用 DTO 规范层间数据交互,防止多余字段污染数据库,并提供了基于 Express.js 和 express-validator 的具体代码实现示例。

文章目录

什么是 DTO

DTO 是 data transfer object 的缩写,中文意为“数据传输对象”。它被定义为一种包含一组值或字段的容器,其核心作用在于封装数据,而非规定数据在系统各层之间传递的具体逻辑。

许多开发者容易混淆数据库模型(Model)与 DTO 的概念。需要明确区分的是:DTO 专注于数据的传输与操作,而数据库模型则专门用于处理数据的持久化存储。

什么时候使用 DTO

在 TypeScript 或 Node.js 开发复杂应用程序时,使用 DTO 来表示数据及其在各层之间的传输方式,已被证明是正确且必要的做法。

这种实践同样适用于纯 JavaScript 或 Node.js 开发环境。引入 DTO 能够有效避免代码结构恶化,保持代码的清晰与可维护性。

为什么要在 Javascript 中使用 DTO

假设你正在使用 JavaScript 开发动态页面,并通过 Node.js 构建 REST API。起初,你创建了数据模型,使用“express-validator”进行数据验证,并定义了路由和中间件,系统运行正常。

然而,随着业务需求变更和代码频繁迭代,可能会出现多个服务或多个 API 以不同方式使用同一个数据模型的情况。如果在每个服务中为了将数据从控制器(Controller)层传递到服务(Service)层,再传递至负责持久化的层而反复复制字段,代码将变得混乱。当一段时间后回顾代码,若无法清晰辨认哪些数据应传递给服务层、哪些数据应从服务层返回,此时引入 DTO 便成为必要。

另一种情况是,若在没有严格 Schema 约束下连接 Firebase 等文档型数据库,并将 JSON 数据作为端点,仅使用“express-validator”进行部分验证,随后将数据传递至服务层并最终持久化。此时,你期望的字段结构可能如下所示:

{
    username: String,
    email: String,
    password: String
}

如何确保 API 消费者不会发送超出定义范围的字段?例如,API 调用者可能会发送包含额外字段的数据:

{
  "username": "test",
  "email": "test@gmail.com",
  "password": "specificPass",
  "birthDate": "2022-05-09T20:12:13.318Z"
}

如你所见,你可以发送验证规则中未定义的字段 birthDate。这将违反服务层的约定,且这些多余数据会被传递至持久化层并保存至数据库,造成数据污染。

此外,假设存在一个同时使用相同服务层的 API 和 WebSocket 连接,如何为两者定义统一的验证逻辑?若不在 DTO 中统一规范,最终可能导致在两种通信方式中重复定义公开数据,增加维护成本。

在上述所有场景中,DTO 都是解决方案。其核心理念简单明了:通过 DTO 明确描述数据在各层接收和公开的方式,实现数据流的标准化控制。

实施和示例

最初,Express.js 的路由定义可能如下所示:

router.post("/user/register", validations, registerController);

我们将使用 express-validator 进行如下验证逻辑:

const validations = [
  body("username").exists().isString().notEmpty(),
  body("email").exists().isEmail(),
  body("password").exists().isString().notEmpty(),
]

随后,控制器或处理程序代码大致如下:

const registerController = (req, res) => {
  const result = await userService.registerUser(req.body);
  return res.status(200).json(result);
}

简单的服务层实现可能如下:

const registerUser = (userData) => {
  userPersistenceLayer.add(userData);
}

现在,让我们定义基础 DTO。在此之前,需明确两个关键事实:

  • 第一,DTO 仅用于数据传输,数据库模型仅用于数据持久化。
  • 第二,应将 DTO 视为一种约定规范,用于定义与其他模块交互时的字段结构。该规范中明确定义了允许存在的字段。
class RegisterUserDTO{
  username;
  email;
  password;
​
  constructor(data) {
    this.username = data.username;
    this.email = data.email;
    this.password = data.password;
  }
}

基于此,我们可以回到服务层,并使用定义的 DTO 进行重构:

const registerUser = (userData) => {
  userPersistenceLayer.add(new RegisterUserDTO(userData));
}

如该模式所示,通过 DTO 我们严格控制了数据传递的方式,确保了仅允许特定字段传递至其他层。此外,还可以在 DTO 中设置 getter 和 setter,以便根据需要在序列化或转换数据时进行自定义处理。

希望以上内容能帮助你清晰、顺畅地理解 DTO 模式的应用。

到此这篇关于浅聊为什么在Javascript中使用 DTO的文章就介绍到这了,更多相关在Javascript中使用 DTO内容请搜索本站以前的文章或继续浏览下面的相关文章希望大家以后多多支持本站!

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章