【问题标题】:DDD: Domain Objects StructureDDD:领域对象结构
【发布时间】:2021-11-25 18:56:21
【问题描述】:

我是 DDD 新手,想清楚了解每个域对象的结构和作用:

  1. 聚合根:

    1.1。客户端可以与域对象交互的唯一接触点,客户端不应该能够修改或创建新的实体或值对象白化聚合根? (是/否)

    1.2。聚合根可以只包含值对象吗?例如用户根,据我所知,它只包含地址、电话、值对象的东西。那么当你的聚合根只包含值对象时,它是否是糟糕设计的标志?它应该只包含实体并通过实体与值对象交互吗?

  2. 实体:实体应该只包含值对象吗?或者它也可以包含其他实体?可以举个简单的例子吗?

  3. 值对象:我应该继续将每个原始类型封装在一个值对象中吗?我可以深入,将每个原始类型都作为值对象,例如:PhoneNumber 可以是字符串或包含国家代码、数字的值对象。同样的事情可以应用于所有其他原始类型值,例如姓名、电子邮件。那么在哪里画线呢?在哪里说“Ok I'm going to deep”,或者说深入是做 DDD 的正确方式?

  4. 工厂:我真的需要它们吗?我可以继续在域对象中编写一个静态方法,它更准确地知道如何构造它,我做错了吗?

很抱歉问了这么长的问题,但尽管继续阅读,我还是觉得有点失落,如果你能帮助我,我会很高兴。

【问题讨论】:

    标签: domain-driven-design entities aggregateroot value-objects


    【解决方案1】:

    我会尽力回答你所有的问题:

    1.1。客户端可以与域对象交互的唯一接触点,客户端不应该能够修改或创建新的实体或值对象白化聚合根? (是/否)

    实体存在于 AR 中,允许客户端创建它们会违反封装,因此对于您是正确的实体,AR 创建自己的实体,这些实体不会暴露在外部(可能是副本/不可变视图) .

    另一方面,值对象通常是不可变的,因此将它们作为数据输入提供给 AR 并没有什么坏处。

    一般来说,所有的修改都需要通过 AR,以便 AR 知道修改。在特殊情况下,当无法通过根时,AR 可以通过侦听内部实体引发的事件来检测其集群内的修改。

    1.2。聚合根可以只包含值对象吗?例如用户根,据我所知,它只包含地址、电话、值对象的东西。那么当你的聚合根只包含值对象时,它是否是糟糕设计的标志?它应该只包含实体并通过实体与值对象交互吗?

    尽可能多地偏爱价值对象。将 AR 的所有部分都建模为值并不罕见。但是,没有限制或法律规定 AR 是否应该只有值或实体,请使用适合您用例的组合。

    实体:实体应该只包含值对象吗?或者它也可以包含其他实体?可以举个简单的例子吗?

    答案同上,没有限制也没有法律。

    值对象:我应该继续将每个原始类型封装在一个值对象中吗?我可以深入,将每个原始类型都作为值对象,例如:PhoneNumber 可以是字符串或包含国家代码、数字的值对象。同样的事情可以应用于所有其他原始类型值,例如姓名、电子邮件。那么在哪里画线呢?在哪里说“Ok I'm going to deep”,或者说深入是做 DDD 的正确方式?

    在我的经验中,原始的痴迷比价值对象的痴迷更糟糕。一般来说,包装一个值的成本非常低,所以当有疑问时,我会建模一个显式类型。这可以为您节省大量的重构工作。

    工厂:我真的需要它们吗?我可以继续在域对象中编写一个静态方法,它更准确地知道如何构造它,我做错了吗?

    AR 上的静态工厂方法很常见,因为它更具有表现力并更严格地遵循 UL。例如,我今天刚刚建模为我们必须“开始小组审核”的用例。实现了GroupAudit.start 静态工厂方法。

    其他 AR 的 AR 上的工厂方法也很常见,例如 var post = forum.post(author, content),其中 Post 是独立于 Forum 的 AR。

    当流程需要一些复杂的协作者时,您可能会考虑使用独立工厂,因为您可能不希望客户知道如何提供和设置这些协作者。

    【讨论】:

      【解决方案2】:

      我是 DDD 新手,想清楚了解每个域对象的结构和作用

      您最好的起点是“蓝皮书”(Evans,2003 年)。

      对于这个问题,需要回顾的两个重要章节是第 5 章(“用软件表达的模型”)和第 6 章(“领域对象的生命周期”)。

      ENTITIES 和 VALUE OBJECTS 是第 5 章中描述的两种模式,也就是说,它们是我们在建模领域时经常出现的模式。 TL;DR 版本:ENTITIES 用于表示域中随时间变化的关系。 VALUE OBJECTS 是特定领域的数据结构。

      AGGREGATES 和 FACTORIES 是第 6 章中描述的模式,也就是说,它们是在我们试图管理域对象的生命周期时经常出现的模式。对域实体的修改可能分布在多个会话中是很常见的,因此我们需要考虑过去如何存储信息并在未来重新加载该信息。


      客户端可以与域对象交互的唯一接触点,客户端不应该能够修改或创建新的实体或值对象白化聚合根?

      灰色区域。 “创造模式很奇怪。”理论是您总是通过聚合根将信息复制到域模型中。但是当你需要的聚合根还不存在时,那怎么办?人们在这里使用许多不同的模式来从无到有创建新的根实体。

      也就是说,我们不希望应用程序直接与聚合的内部设计耦合。这是标准的“最佳实践”OO,应用程序代码与模型的接口耦合,而不与模型的实现/数据结构耦合。

      聚合根可以只包含值对象吗?

      聚合中根实体的定义可能包括对同一聚合中其他实体的引用。 Evans 明确提到“根以外的实体”;为了与根以外的实体共享信息,必须有某种方法可以遍历从根到这些非根实体的引用。

      实体应该只包含值对象吗?

      实体的定义可能包括对同一聚合中其他实体(包括根实体)的引用。

      我应该继续将每个原始类型封装在一个值对象中吗?

      “视情况而定”——在像 java 这样的语言中,值对象是一种可供性,使编译器可以很容易地为您提供有关某些类型错误的早期反馈。

      如果您有验证问题,则尤其如此。我们希望验证(或解析)信息一次,而不是在每个地方重复相同的检查(重复),并且使已验证数据与未验证数据可检测到不同可降低未验证数据泄漏到未正确处理的代码路径中的风险.

      如果您决定底层数据结构需要改进,拥有值对象还可以减少需要更改的地方的数量,并且值对象为您提供了一个容易猜到的地方来放置与该值相关的函数/方法。

      工厂:我真的需要它们吗?

      是的,而且……

      我可以继续在域对象中编写一个静态方法

      ...没关系。基本思想:如果从足够多的信息集创建域对象很复杂,我们希望将这种复杂性集中在一个地方,可以在需要的地方调用它。这并不一定意味着我们需要一个名词。一个函数就好了。

      当然,如果您的域对象并不复杂,那么“只”使用对象构造函数/初始化器。

      【讨论】:

        猜你喜欢
        • 2011-04-03
        • 2016-12-06
        • 2022-11-18
        • 1970-01-01
        • 2017-02-01
        • 1970-01-01
        • 1970-01-01
        • 2011-05-17
        • 2019-07-19
        相关资源
        最近更新 更多