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

.NET Core API设计:DTO该选用record还是ValueObject?

.NET Core API中DTO:Record 与 ValueObject 的选择

核心判断标准

选 Record 的场景

  • 纯数据传输需求:比如API的请求/响应DTO,这类对象只负责在不同层级(前端、API层、服务层)之间传递数据,不需要承载业务逻辑,像你示例里的LoginRequest和AuthenticationResponse就属于这类。
  • 想要默认的基于值的相等性:Record会自动根据所有属性的值判断对象是否相等,对比两个数据传输对象内容是否一致时很省心。
  • 快速构建不可变数据对象:用 positional record 可以一行定义所有属性,自动生成构造函数、ToString、Equals等方法,省去大量样板代码。

选 ValueObject 的场景

  • 对象带有业务语义和行为:比如代表金额的Money(包含数值+币种,还要实现加减、币种转换等方法)、代表地址的Address,这类对象是业务概念的封装,不是单纯的数据容器。
  • 需要内置业务规则验证:比如Email对象要在创建时就校验格式合法性,PhoneNumber要验证号码规则,这些逻辑应该内聚在对象内部,而不是在外部零散校验。
  • 无标识的业务概念:ValueObject没有唯一ID,两个属性完全相同的对象在业务上视为同一个,比如两个地址完全一致的Address,不需要区分它们的身份。

你的示例场景分析

你给出的LoginRequest和AuthenticationResponse是典型的API数据传输场景,用Record完全适配需求:

LoginRequest 定义

public record LoginRequest
(
    string Email,
    string Password
);

AuthenticationResponse 定义

public record AuthenticationResponse
(
    Guid Id,
    string FirstName,
    string LastName,
    string Email,
    string Token
);

控制器使用示例

[HttpPost("login")]
public IActionResult Login(LoginRequest request)
{
    var authResult = _authenticationService.Login(request.Email, request.Password); //返回数据类
    var response = new AuthenticationResponse(
        authResult.Id,
        authResult.FirstName,
        authResult.LastName,
        authResult.Email,
        authResult.Token
        );
    return Ok(response);
}

这两个Record满足了不可变性(positional record的属性默认是init-only)、值相等性、代码简洁的要求,完全适配纯数据传输的场景。

总结

  • 纯数据传输的DTO(请求/响应对象)优先用Record,省心省力还符合场景需求。
  • 带有业务逻辑、语义的不可变对象,优先用ValueObject,它是业务领域概念的封装,更贴合DDD设计思想。

内容的提问来源于stack exchange,提问作者Jivopis

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 17:40:02