DDD:域对象结构
DDD: Domain Objects Structure
我是 DDD 新手,想清楚地了解每个领域对象的结构和作用:
聚合根:
1.1。客户端可以与域对象交互的唯一联系点,客户端不应该能够修改或创建新的实体或值对象来覆盖聚合根? (Yes/No)
1.2。聚合根可以只包含值对象吗?例如用户根,它只包含地址,phone,据我所知,这些都是值对象。那么,当您的聚合根仅包含值对象时,这是不是设计不良的标志?它应该只包含实体并通过实体与值对象交互吗?
实体:实体是否只包含值对象?或者它也可以包含其他实体?你能给我一个简单的例子吗?
值对象:我应该继续将每个原始类型封装在一个值对象中吗?我可以深入研究,将每个基本类型都作为一个值对象,例如:PhoneNumber 可以是一个字符串,也可以是一个包含国家代码、数字的值对象。同样的事情可以应用于所有其他原始类型值,例如姓名、电子邮件。那么在哪里划清界限呢?在哪里说“好吧,我要深入”,或者深入是做 DDD 的正确方法?
工厂:我真的需要它们吗?我可以继续在域对象中编写一个静态方法,它更准确地知道如何构造它,我做错了吗?
抱歉问了这么长的问题,但尽管继续阅读,我还是有点迷茫,如果你能帮助我,我会很高兴。
我会尽力回答您的所有问题:
1.1。客户端可以与域对象交互的唯一联系点,客户端不应该能够修改或创建新的实体或值对象来覆盖聚合根? (Yes/No)
实体存在于 AR 中,允许客户端创建它们会违反封装,因此对于实体,您是正确的,AR 创建自己的实体,不会暴露在外部(copies/immutable 观看次数可能是)。
另一方面,值对象通常是不可变的,因此将它们作为数据输入提供给 AR 没有坏处。
一般来说所有的修改都需要经过AR,这样AR才能知道修改。在特殊情况下,AR 可以通过侦听内部实体引发的事件来检测其集群内的修改,而这是不切实际的。
1.2。聚合根可以只包含值对象吗?例如用户根,它只包含地址,phone,据我所知,这些都是值对象。那么,当您的聚合根仅包含值对象时,这是不是设计不良的标志?它应该只包含实体并通过实体与值对象交互吗?
尽可能多地支持值对象。将 AR 的所有部分建模为值并不罕见。但是,没有任何限制或法律规定 AR 是否应该只有值或实体,请使用适合您的用例的组合。
实体:实体是否只包含值对象?或者它也可以包含其他实体?你能给我一个简单的例子吗?
同上答案,无限制,无规律
值对象:我应该继续将每个原始类型封装在一个值对象中吗?我可以深入研究,将每个基本类型都作为一个值对象,例如:PhoneNumber 可以是一个字符串,也可以是一个包含国家代码、数字的值对象。同样的事情可以应用于所有其他原始类型值,例如姓名、电子邮件。那么在哪里划清界限呢?在哪里说“好吧,我要深入”,或者深入是做 DDD 的正确方法?
在我的经验中,原始的迷恋比价值对象的迷恋更糟糕。包装一个值的成本通常很低,所以当有疑问时,我会建模一个显式类型。这可以为您节省大量重构工作。
工厂:我真的需要它们吗?我可以继续在域对象中编写一个静态方法,它更准确地知道如何构造它,我做错了吗?
AR 上的静态工厂方法作为一种更具表现力和更紧密地遵循 UL 的手段非常普遍。例如,我今天刚刚建模为我们必须“开始小组审计”的用例。实现了 GroupAudit.start
静态工厂方法。
AR 上用于其他 AR 的工厂方法也很常见,例如 var post = forum.post(author, content)
,其中 Post
是不同于 Forum
的单独 AR。
当流程需要一些复杂的协作者时,您可以考虑使用独立工厂,因为您可能不希望客户知道如何提供和设置这些协作者。
I'm new to DDD and I want to clearly understand each domain object structure and role
最好的起点是“蓝皮书”(埃文斯,2003 年)。
对于这个问题,需要复习的两个重要章节是第 5 章(“用软件表示的模型”)和第 6 章(“领域对象的生命周期”)。
ENTITIES 和 VALUE OBJECTS 是第 5 章中描述的两种模式,也就是说它们是我们在对域进行建模时通常会出现的模式。 TL;DR 版本:ENTITIES 用于表示域中随时间变化的关系。值对象是特定于域的数据结构。
AGGREGATES 和 FACTORIES 是第 6 章中描述的模式,也就是说它们是当我们试图管理域对象的生命周期时通常出现的模式。对域实体的修改可能分布在多个会话中是很常见的,因此我们需要考虑过去如何存储信息并在未来重新加载该信息。
The only contact point the client can interact with the domain objects, the client should not be able to modify or create new Entities or value objects whiteout the aggregate root?
灰色地带。 “创造模式很奇怪。”从理论上讲,您总是通过聚合根将信息复制到域模型中。但是当你需要的聚合根还不存在时,那又如何呢?人们在这里使用许多不同的模式来从无到有地创建新的根实体。
也就是说 - 我们不希望应用程序直接耦合到聚合的内部设计。这是标准的“最佳实践”OO,应用程序代码耦合到模型的接口而不耦合到模型的 implementation/data 结构。
Can an aggregate root contain only value objects ?
聚合中根实体的定义可能包括对同一聚合中其他实体的引用。埃文斯明确提到“根以外的实体”;为了与根以外的实体共享信息,必须有一些方法来遍历从根到这些非根实体的引用。
Shall the entities contain only value objects?
实体的定义可能包括对同一聚合中其他实体(包括根实体)的引用。
shall I go ahead and encapsulate every primitive type in an value object?
“视情况而定”- 在像 java 这样的语言中,值对象是一种可供性,使编译器可以轻松地为您提供有关某些类型错误的早期反馈。
如果您有验证问题,尤其如此。我们希望验证(或解析)信息一次,而不是在每个地方重复相同的检查(重复),并且验证数据与未验证数据的可检测差异降低了未验证数据泄漏到未正确处理的代码路径中的风险.
如果您决定底层数据结构需要改进,拥有一个值对象还可以减少需要更改的地方的数量,并且值对象为您提供了一个容易猜到的地方来放置与该值相关的 functions/methods .
Factories: Do I really need them?
是的,而且...
I can go ahead and write an static method within the domain object
...没关系。基本思想:如果从如此充足的信息集创建域对象很复杂,我们希望将这种复杂性放在一个地方,可以在我们需要的地方调用它。这并不一定意味着我们需要一个名词。一个函数就好了
当然,如果您的域对象并不复杂,那么“只需”使用对象 constructor/initializer。
我是 DDD 新手,想清楚地了解每个领域对象的结构和作用:
聚合根:
1.1。客户端可以与域对象交互的唯一联系点,客户端不应该能够修改或创建新的实体或值对象来覆盖聚合根? (Yes/No)
1.2。聚合根可以只包含值对象吗?例如用户根,它只包含地址,phone,据我所知,这些都是值对象。那么,当您的聚合根仅包含值对象时,这是不是设计不良的标志?它应该只包含实体并通过实体与值对象交互吗?
实体:实体是否只包含值对象?或者它也可以包含其他实体?你能给我一个简单的例子吗?
值对象:我应该继续将每个原始类型封装在一个值对象中吗?我可以深入研究,将每个基本类型都作为一个值对象,例如:PhoneNumber 可以是一个字符串,也可以是一个包含国家代码、数字的值对象。同样的事情可以应用于所有其他原始类型值,例如姓名、电子邮件。那么在哪里划清界限呢?在哪里说“好吧,我要深入”,或者深入是做 DDD 的正确方法?
工厂:我真的需要它们吗?我可以继续在域对象中编写一个静态方法,它更准确地知道如何构造它,我做错了吗?
抱歉问了这么长的问题,但尽管继续阅读,我还是有点迷茫,如果你能帮助我,我会很高兴。
我会尽力回答您的所有问题:
1.1。客户端可以与域对象交互的唯一联系点,客户端不应该能够修改或创建新的实体或值对象来覆盖聚合根? (Yes/No)
实体存在于 AR 中,允许客户端创建它们会违反封装,因此对于实体,您是正确的,AR 创建自己的实体,不会暴露在外部(copies/immutable 观看次数可能是)。
另一方面,值对象通常是不可变的,因此将它们作为数据输入提供给 AR 没有坏处。
一般来说所有的修改都需要经过AR,这样AR才能知道修改。在特殊情况下,AR 可以通过侦听内部实体引发的事件来检测其集群内的修改,而这是不切实际的。
1.2。聚合根可以只包含值对象吗?例如用户根,它只包含地址,phone,据我所知,这些都是值对象。那么,当您的聚合根仅包含值对象时,这是不是设计不良的标志?它应该只包含实体并通过实体与值对象交互吗?
尽可能多地支持值对象。将 AR 的所有部分建模为值并不罕见。但是,没有任何限制或法律规定 AR 是否应该只有值或实体,请使用适合您的用例的组合。
实体:实体是否只包含值对象?或者它也可以包含其他实体?你能给我一个简单的例子吗?
同上答案,无限制,无规律
值对象:我应该继续将每个原始类型封装在一个值对象中吗?我可以深入研究,将每个基本类型都作为一个值对象,例如:PhoneNumber 可以是一个字符串,也可以是一个包含国家代码、数字的值对象。同样的事情可以应用于所有其他原始类型值,例如姓名、电子邮件。那么在哪里划清界限呢?在哪里说“好吧,我要深入”,或者深入是做 DDD 的正确方法?
在我的经验中,原始的迷恋比价值对象的迷恋更糟糕。包装一个值的成本通常很低,所以当有疑问时,我会建模一个显式类型。这可以为您节省大量重构工作。
工厂:我真的需要它们吗?我可以继续在域对象中编写一个静态方法,它更准确地知道如何构造它,我做错了吗?
AR 上的静态工厂方法作为一种更具表现力和更紧密地遵循 UL 的手段非常普遍。例如,我今天刚刚建模为我们必须“开始小组审计”的用例。实现了 GroupAudit.start
静态工厂方法。
AR 上用于其他 AR 的工厂方法也很常见,例如 var post = forum.post(author, content)
,其中 Post
是不同于 Forum
的单独 AR。
当流程需要一些复杂的协作者时,您可以考虑使用独立工厂,因为您可能不希望客户知道如何提供和设置这些协作者。
I'm new to DDD and I want to clearly understand each domain object structure and role
最好的起点是“蓝皮书”(埃文斯,2003 年)。
对于这个问题,需要复习的两个重要章节是第 5 章(“用软件表示的模型”)和第 6 章(“领域对象的生命周期”)。
ENTITIES 和 VALUE OBJECTS 是第 5 章中描述的两种模式,也就是说它们是我们在对域进行建模时通常会出现的模式。 TL;DR 版本:ENTITIES 用于表示域中随时间变化的关系。值对象是特定于域的数据结构。
AGGREGATES 和 FACTORIES 是第 6 章中描述的模式,也就是说它们是当我们试图管理域对象的生命周期时通常出现的模式。对域实体的修改可能分布在多个会话中是很常见的,因此我们需要考虑过去如何存储信息并在未来重新加载该信息。
The only contact point the client can interact with the domain objects, the client should not be able to modify or create new Entities or value objects whiteout the aggregate root?
灰色地带。 “创造模式很奇怪。”从理论上讲,您总是通过聚合根将信息复制到域模型中。但是当你需要的聚合根还不存在时,那又如何呢?人们在这里使用许多不同的模式来从无到有地创建新的根实体。
也就是说 - 我们不希望应用程序直接耦合到聚合的内部设计。这是标准的“最佳实践”OO,应用程序代码耦合到模型的接口而不耦合到模型的 implementation/data 结构。
Can an aggregate root contain only value objects ?
聚合中根实体的定义可能包括对同一聚合中其他实体的引用。埃文斯明确提到“根以外的实体”;为了与根以外的实体共享信息,必须有一些方法来遍历从根到这些非根实体的引用。
Shall the entities contain only value objects?
实体的定义可能包括对同一聚合中其他实体(包括根实体)的引用。
shall I go ahead and encapsulate every primitive type in an value object?
“视情况而定”- 在像 java 这样的语言中,值对象是一种可供性,使编译器可以轻松地为您提供有关某些类型错误的早期反馈。
如果您有验证问题,尤其如此。我们希望验证(或解析)信息一次,而不是在每个地方重复相同的检查(重复),并且验证数据与未验证数据的可检测差异降低了未验证数据泄漏到未正确处理的代码路径中的风险.
如果您决定底层数据结构需要改进,拥有一个值对象还可以减少需要更改的地方的数量,并且值对象为您提供了一个容易猜到的地方来放置与该值相关的 functions/methods .
Factories: Do I really need them?
是的,而且...
I can go ahead and write an static method within the domain object
...没关系。基本思想:如果从如此充足的信息集创建域对象很复杂,我们希望将这种复杂性放在一个地方,可以在我们需要的地方调用它。这并不一定意味着我们需要一个名词。一个函数就好了
当然,如果您的域对象并不复杂,那么“只需”使用对象 constructor/initializer。