5

当操作在概念上不属于任何实体或值对象时,我们应该创建一个域服务,而不是将行为强制到一个对象中。

服务的接口应该根据领域模型的其他元素来定义。换句话说,服务的参数和返回值应该是领域对象

a) 为什么域服务应该/必须使用域对象作为参数和返回值?

b) 为什么 DDD 也不要求 Entities 和 Value Objects 的方法使用域对象作为参数和返回值?为什么这个约束只放在服务上?

谢谢

欧乐汇:

1)

这两个约束都促进了不变性和函数式风格

a) 这两个约束如何促进不变性?

b) 什么是功能风格?

c)所以我们应该尝试(因为它可能并不总是可以使用 force )强制服务使用域对象作为参数和返回值,即使该服务(即行为)接受/返回可能更自然非域对象?

2)

实体和值对象组成更多的原始类型以形成复杂类型,并且某些行为可能取决于原始参数。

那么,在大多数情况下,它们的行为(即它们的操作)对原始类型(即使用原始类型作为参数)进行操作是否是由于域实体/值对象的某种内在特征?如果是,那么在大多数情况下,这种内在特征是否存在于域对象中,但很少存在于域服务中?

第二次更新:

这两个约束如何促进不变性?

这个想法是域服务不会改变状态,并且所有状态更改都通过参数显式地进行。

a) 不改变自己的状态或某些域对象的状态?由于域服务应该是无状态的,我假设你的意思是它不应该改变 DO 的状态?换句话说,服务通过确保它打算修改的任何 DO 都作为参数传递给它(即传递给它的操作)来促进不变性?

b) 但是如果要被服务修改的 DO 没有作为参数传递,那么我们说域服务改变了这个 DO 的状态?

c) 改变 DO 的状态被认为是一件坏事的原因是因为它不会提高清晰度(即,在查看服务操作的签名时并不是很明显,哪些 DO 将通过手术 )?

d) 如果域服务要修改作为参数传递给它的 DO 的状态,如果它将用于更改此 DO 状态的值也作为参数传递给服务,那将是理想的吗?如果是,是因为它促进了清晰度还是……?

2)我仍然不明白返回值与参数类型相同如何促进不变性?

欧乐外汇 3

一个)

域服务可以通过返回对象的新实例而不是修改传入的对象来避免状态突变。

不是一个问题,更多的是一种观察,但有些困难理解为什么这种服务行为在大多数领域模型中很常见,甚至这种行为是在对领域建模时自然产生的,还是我们必须将它强加到概念中?!

b)

是的,尽管在这种情况下,域对象自己变异会更好。

DO 自身发生变异的主要原因是因为在特定 DO 上执行变异的代码集中在一个地方,所以如果我们需要检查这段代码,我们知道在哪里寻找它?

4

1 回答 1

8

a) 这不是一个严格的限制,但提供了某些优势。该规则背后的想法是域服务包含补充现有实体和值对象的功能。另一个非严格约束是域服务方法的参数和返回值都属于同一类型的操作的闭包。这两个约束都促进了不变性和函数式风格,从而减少了副作用并更容易推理代码、重构代码等。

可以有一个域服务方法接受既不是实体也不是值对象的原始类型。但是,大量使用原始类型可能会导致原始痴迷

b) 该约束可以在一定程度上应用于实体和值对象级别。实体和值对象组成更多的原始类型以形成复杂类型,并且某些行为可能取决于原始参数。这个原始参数本身可以变成一个值对象。

更新

刚从 DDD 聚会回来,我有机会与实施领域驱动设计的Vaughn Vernon讨论这个问题。他同意指定的约束条件并不严格。换句话说,在某些情况下,域服务方法完全可以通过原始类型参数化。

这两个约束如何促进不变性?

这个想法是域服务不会改变状态,并且所有状态更改都通过参数显式地进行。这就是纯函数的本质。鉴于域服务补充了实体,它们的方法应该用这些术语来表达。

什么是功能风格?

我指的是函数式编程。函数式编程通常需要不变性和纯函数。函数式方法的另一个特点是与命令式形成对比的声明式风格。

所以我们应该尝试(因为它可能并不总是可以使用 force )来强制 Service 使用域对象作为参数和返回值

不。如果原始类型足以满足操作,则没有理由将其强制转换为其他类型。实体和值对象的使用只是一个指导方针,有些人更喜欢比其他人更严格。例如,有些使用显式类型来表示每个实体的身份。int因此,您将创建一个值对象,而不是使用它OrderId来表示订单的身份。

那么是不是由于域实体/值对象的某种内在特征在大多数情况下它们的行为(即它们的操作)对原始类型进行操作(即使用原始类型作为参数)?

我不会说它是 DDD 固有的。我指的是更一般的组合概念——复杂实体(非 DDD)由更简单的实体组成。因此,对复杂实体的操作将根据组成部分来表示是有道理的。

更新 2

a) 域服务可以通过返回对象的新实例而不是修改传入的对象来避免状态突变。这样,方法的签名完全描述了它的作用,因为没有副作用。

b) 域服务可以改变传递给它的对象的状态,在这种情况下,返回类型可能是完整的。然而,这是不太可取的 - DO 改变自己的状态会更好。

c) 是的,这是其中的一部分。不变性和纯度使您可以重构代码,就像使用代数方程分解代数一样。另一个原因是它使对代码的推理更容易,因为如果您查看一段不可变数据,您可以确定它在其范围的其余部分不会改变。

d) 是的,尽管在这种情况下,域对象自己变异会更好。此突变将由周围的应用程序服务调用。很多时候,我将域服务传递给实体行为方法,为它们提供他们无法直接访问的功能。

e) 操作闭包的概念本身并不能促进不变性,但它是不可变代码的一个特征。原因是,如果域服务方法接受 T 类型的值并返回 T 类型的值,则它可以指示它返回由封装操作产生的新 T 值。这是不变性的一个特征,因为操作产生的变化被明确地作为一个新对象。

更新 3

a) 这与传统 OOP 的关系比与 DDD 的关系更大。OOP 试图将移动的部分隐藏在对象后面——封装。FP 试图最小化移动部分 - 不变性。在某些情况下,不可变性可以被视为更“自然”。例如,在以事件为中心的场景中,事件是不可变的,因为它们是已发生事件的记录。你不会改变已经发生的事情,但你可以创建补偿动作。

b) 同样,这与 OOP 的关系比 DDD 更多,并且基于信息专家模式,该模式本质上表明数据上的行为应尽可能接近该数据。在 DDD 中,这意味着一个实体应该尽可能地封装所包含的数据,以确保其自身的完整性。

于 2013-01-14T21:54:00.630 回答