什么时候适合将 DTO 映射回对应的实体

When is it appropriate to map a DTO back to its Entity counterpart

根据我所阅读和实施的内容,DTO 是保存数据模型值子集的对象,在大多数情况下,这些是不可变对象。

如果我需要将新值或更改传回数据库怎么办?

我应该在表示层中直接使用 DAL 中的数据 model/actual 实体吗?

或者我应该创建一个可以从表示层传递到业​​务层的 DTO,然后将其转换为实体,然后通过 ORM 调用在数据库中更新。这是写太多代码了吗?如果表示层没有数据模型的概念,我假设这是必需的。如果我们采用这种方法,我是否应该在提交更改之前在 BLL 层再次获取对象?

我的观点是,DTO 代表构成聚合根与外部世界交互基础的合同(或消息,如果您愿意的话)。它们在域中定义,AR 需要能够处理传入实例并提供传出实例。 (请注意,在大多数情况下,DTO 实例要么由 AR 提供,要么由 AR 处理,但不会同时由 AR 处理,因为让一个 DTO 双向流动通常违反了关注点分离.)

同时,AR 负责提供处理 DTO 中包含的数据的业务逻辑。表示层(或任何其他参与者,包括数据访问层,就此而言)可以自由地将它想要的任何乱码放入 DTO 并请求 AR 处理它,AR 必须能够解释 DTO 的内容作为胡言乱语并引发异常。

由于这个要求,简单地将 DTO 映射回其对应的实体是不合适的。

DTO 必须始终通过 AR 中的逻辑进行处理,以影响实体中的更改,可能将其带入 DTO 描述的状态。

Should I work directly with the data model/actual entity from my DAL in my Presentation layer?

这适用于中小型项目。但是,如果您有一个包含 5 个以上开发人员的大型项目,其中不同的层被分配给不同的团队,那么该项目将受益于使用 DTO 将数据层与表示层分开。

中间有一个 DTO,表示层的任何更改都不会影响数据层(反之亦然)

Or should I create a DTO that can be passed from the presentation layer to the business layer then convert it to an entity, then be updated in the DB via an ORM call. Is this writing too much code? I'm assuming that this is needed if the presentation layer has no concept of the data model. If we are going with this approach, should I fetch the object again at the BLL layer before committing the change?

对于创建一个新实体,这是通常的方法(例如"new user")。要更新现有实体,您无需将 DTO 转换为实体,而是获取现有实体、映射新值然后启动 ORM 更新。

UpdateUser(UserDto userDto)
{
    // Fetch
    User user = userRepository.GetById(userDto.ID);

    // Map
    user.FirstName = userDTO.FirstName;
    user.LastName = userDTO.LastName;

    // ORM Update
    userRepository.Update(user);
    userRepository.Commit();
}

对于有很多开发人员的大型项目,与其提供的解耦的巨大优势相比,编写过多代码的缺点是微乎其微的。

查看我的 post 关于 Why use a DTO

一些想法:

  • DTO 是一个加载的术语,但因为它代表数据 传输 对象,我更多地把它看作是一个纯粹的技术,可能是可序列化的 container 从一个点到另一个点获取数据,通常是跨层或跨层。在处理业务问题的层内,例如 DDD 中的域层,这些循环的小数据结构往往被命名为值对象,因为它们具有业务意义并且是域通用语言的一部分。 DTO和Value Object之间有各种微妙的区别,比如你通常不需要比较DTO,而比较和相等是VO中的一个重要问题(如果两个VO封装的数据相等,则它们相等)。

  • DDD 强调富领域模型的思想。这意味着您通常不会简单地将 DTO 的一对一映射到域实体,而是尝试将业务行为建模为实体中的意图揭示方法。例如,您不会使用 setter 来修改 UserStreetCityZipCode,而是调用 moveTo(Address newAddress) 方法,Address 是域层中声明的值对象。

  • DTO通常不会到达领域层,而是通过应用层的过滤器。它可以是控制器或专用的应用程序服务。应用层对象知道如何将从客户端获得的 DTO 转换为对域层实体(通常是从存储库加载的聚合根)的正确调用。上面的另一个改进级别是构建 tasked-based UIs 用户不发送以数据为中心的 DTO,而是发送反映其最终目标的命令。

因此,将 DTO 映射到实体并不是真正的 DDD 做事方式,它更像是一种面向 CRUD 的方法。