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

use*_*291 4 domain-driven-design

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

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

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

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

谢谢

EULERFX:

1)

这两种约束都促进了不变性和功能性风格

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

b)什么是功能风格?

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

2)

实体和值对象组成更原始的类型以形成复杂类型,并且一些行为可能依赖于原始参数.

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

第二次更新:

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

这个想法是域服务不会改变状态,所有状态更改都通过参数显式化.

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

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

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

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

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

EULERFX 3

一个)

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

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

b)

是的,虽然在这种情况下,域对象改变自身会更好.

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

eul*_*rfx 6

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

可以使用接受基本类型的域服务方法,该基本类型既不是实体也不是值对象.然而,原始类型的广泛使用可能导致原始的痴迷.

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

UPDATE

刚从DDD聚会回来,我有机会与Vaughn Vernon 实施领域驱动设计的作者讨论这个问题.他同意指定的约束并不严格.换句话说,存在这样的情况:域服务方法被基元类型参数化是完全可接受的.

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

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

什么是功能风格?

我指的是函数式编程.功能风格的编程通常需要不变性和纯函数.功能性方法的另一个特征是与命令对比的陈述式风格.

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

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

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

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

更新2

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

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

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

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

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

更新3

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

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