为什么域服务必须使用域对象作为参数并返回值?

时间:2013-01-14 20:37:10

标签: domain-driven-design

  

当操作在概念上不属于任何实体或价值时   对象,然后而不是强迫行为进入对象,我们   应该创建域服务。

     

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

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

b)为什么DDD还要求实体和值对象的方法将域对象用作参数并返回值?为什么这个约束只放在服务上呢?

谢谢

EULERFX:

1)

  

这两个限制都促进了不变性和功能性风格

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

b)什么是功能风格?

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

2)

  

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

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

第二次更新:

  
    

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

  
     

这个想法是域服务不会改变状态和所有状态   通过参数明确更改。

a)不改变自己的状态或某个域对象的状态?由于域名服务应该是无国籍的,我认为你的意思是它不应该改变DO的状态?换句话说,服务通过确保将要修改的任何DO作为参数传递给它(即传递给它的操作)来提升 immutability

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

c)改变DO状态被认为是坏事的原因是因为它没有提升清晰度(即,当查看服务操作的签名时,它并不是立即显而易见的, DO会通过操作改变他们的状态吗?

d)如果域服务要修改作为参数传递给它的DO的状态,那么它是理想的,如果它用来改变这个DO状态的值也会作为参数传递服务。如果是的话,是因为它促进了清晰度还是......?

2)我仍然不了解返回值与论证类型相同如何促进不变性?

EULERFX 3

A)

  

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

不是一个问题,更多的是观察,但有些困难理解为什么这种服务行为在大多数领域模型中是常见的,甚至是在建模领域时这种行为是否自然而然或我们必须强迫它进入概念a位?!

b)中

  

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

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

1 个答案:

答案 0 :(得分:6)

a)这不是严格的约束,但具有某些优点。规则背后的想法是域服务包含补充现有实体和值对象的功能。另一个非严格约束是关闭操作,其中域服务方法的参数和返回值都是相同的类型。这两个约束都促进了不变性和功能风格,从而减少了副作用,使得更容易推理代码,重构代码等。

可以使用域服务方法接受既不是实体也不是值对象的基本类型。但是,广泛使用原始类型可能会导致primitive obsession

b)此约束可以在实体和值对象级别应用到某个范围。实体和值对象组成更原始的类型以形成复杂类型,并且一些行为可能依赖于原始参数。这个原始参数本身可以变成一个值对象。

<强>更新

刚从DDD聚会回来,我有机会与Vaughn VernonImplementing Domain-Driven Design作者讨论这个问题。他同意指定的约束并不严格。换句话说,在某些情况下,域服务方法可以完全被原始类型参数化。

  

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

这个想法是域服务不会改变状态,所有状态更改都通过参数显式化。这是pure function的本质。鉴于域服务补充实体,其方法应以这些术语表示。

  

什么是功能风格?

我指的是functional programming。功能风格的编程通常需要不变性和纯函数。功能方法的另一个特点是declarative style与命令形成对比。

  

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

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

  

因此,由于Domain的某种内在特征   在大多数情况下他们的行为(即他们的行为)的实体/价值对象   操作)对原始类型进行操作(即使用原始类型作为   参数)?

我不会说这是DDD固有的。我指的是更为笼统的构思 - 复杂的实体(非DDD)由更简单的构成。通过这种方式,复杂实体上的操作将以组成部分的形式表达。

更新2

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

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

c)是的,这是其中的一部分。不变性和纯度允许您重构代码,就像您将代数方程与替换因子一样。另一个原因是它使得代码的推理变得更容易,因为如果你看一段不可变数据,你就可以确定它在其余范围内没有变化。

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

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

更新3

a)这与传统的OOP有关,而与DDD有关。 OOP试图隐藏对象后面的移动部件 - 封装。 FP试图最小化运动部件 - 不变性。不变性可被视为更多&#34;自然&#34;在某些情况下。例如,在以事件为中心的场景中,事件是不可变的,因为它们是已发生事件的记录。你不会改变发生的事情,但你可以创造补偿行动。

b)同样,这更多地与OOP而不是DDD有关,并且基于information expert pattern,它基本上表明数据上的行为应尽可能接近该数据。在DDD中,这意味着实体应尽可能地封装所包含的数据,以便确保其自身的完整性。