如何使用和创建 DTO 是 OOP 的世界?

How to use and create DTOs is OOP world?

  1. 从业务对象创建 DTO 的正确方法是什么?
  2. 谁应该负责创建它们? BO/DTO 本身来自 BO/some 静态工厂?
  3. 如果我有,它们应该驻留在代码中的什么地方,f.e。我需要 DTO 的一些核心库和特定服务 API 库?在 BO 旁边的核心库中(这似乎不正确)/在特定库中?
  4. 如果我在我的 BO 中封装了字段,DTO 如何获取它们? (显然,如果 BO 不负责创建 DTO)

举个例子,假设我有这样的 Person BO:

class Person
{
    private int age;
    public bool isBigEnough => age > 10;
}

我希望 age 成为 Person 的内部状态,但我仍然需要将我的 BO 传达给某些 api。或者在我的 class 中有私人字段,我想发送到某个地方已经意味着它应该是 public?

  1. 对于如何将 DTO 与带有封装数据的业务 classes 一起使用,是否有任何一般性考虑?

___更新:

除了@Alexey Groshev 提到的方法之外,我还遇到了另一种方法:我们将 BO class 的数据分离到一些具有 public 访问器的数据 class 中。 BO 用它的 api(可能使用组合)包装这个数据,并且在需要时它可以 return 它的状态作为数据 class 作为克隆。所以 dto 转换器将能够访问域对象的状态但不能修改它(因为它只是一个副本)。

我认为问题 5 的答案也将解决其他问题。

Are there any general considerations of how to use DTOs alongside business classes with encapsulated data?

请记住,DTO 仅用于传输数据。不要担心在 DTO 中实施任何类型的规则。它的全部用途是将数据从一个子系统移动到另一个子系统(不是在同一子系统的 类 之间)。数据在目标系统中的使用方式不受您的控制——尽管作为上帝程序员,您天生就知道它将如何使用,但不要让这些知识影响您的设计——因此不应该表达任何假设作为行为或知识访问者——所以,没有 isBigEnough.

有多种选择,但很难推荐任何东西,因为我不知道你的 project/product 的详细信息。不管怎样,我会举几个例子。

  1. 可以使用AutoMapper to map BOs to DTOs and vise versa. I personally dislike this approach, because it's quite difficult (but possible) to keep it under control in medium/large sized projects. People don't usually bother to configure mappings properly and just expose internal state of their objects. For example, your isBigEnough would disappear and age would become public. Another potential risk is that people can map DTOs to/from EF/Hibernate objects. You can find some articles which explain why it's considered to be a bad practice.

  2. 按照您的建议,BO可以自己创建DTO,但是您将如何实现这种方法?您可以向实体添加方法或工厂方法,例如public PersonDto ToDto()。或者您可以添加一个接口,例如public interface IDtoConvertable<T> { T ToDto(); },并选择哪个实体或聚合根将实施它。您的 Person class 看起来像这样 class Person : IDtoConvertable<PersonDto> {... public PersonDto ToDto() {...} }。在这两种情况下,DTO namespace/assembly 必须可供实体访问,这有时可能是个问题,但通常不是什么大问题。 (确保 DTO 无法访问更糟糕的实体。)

  3. (C#) 另一种选择是 return 创建 DTO 的委托。我决定将它与 (2) 分开,因为实体本身并没有真正创建 DTO,而是公开了创建 DTO 的功能。所以,你可以有这样的东西 public Func<PersonDto> ToDto() {...}。您可能想要一个如 (2) 中的界面,但您明白了,不是吗?我喜欢这种方法吗?不,因为它使代码不可读。

如您所见,问题多于答案。我建议您做一些实验,看看哪些对您(您的项目)有效,哪些无效。