【问题标题】:DDD domain services: what should a service class contain?DDD 领域服务:一个服务类应该包含什么?
【发布时间】:2013-07-18 17:31:27
【问题描述】:

在领域驱动设计中,领域服务应该包含自然不属于实体内部的操作。

我的习惯是为每个实体创建一个服务并在其中分组一些方法(Organization 实体和OrganizationService 服务)。

但我越想:OrganizationService 没有任何意义,“组织不是服务,而是事物。

所以现在我必须添加一个将复制整个组织聚合的组织深层复制功能,所以我想将它放在一个服务中。

我应该这样做:OrganizationService::copyOrganization(o)

或者我应该这样做:OrganizationCopyService::copyOrganization(o)

更一般地说:“服务”是一个包含多个操作的抽象概念,还是一个服务是一个具体的操作?

编辑:第一个例子不是很好:

  • StrategyService::apply()/cancel() 还是 StrategyApplicationService::apply()/cancel()? (这里的“应用”与应用层无关;)
  • CarService::wash()CarWashingService::wash()?

在所有这些示例中,最具体的服务名称似乎是最合适的。毕竟,在现实生活中,“洗车服务”是有道理的。但我最终可能会得到很多服务......

*注意:这不是关于意见的问题!这是一个关于领域驱动设计方法的精确、可回答的问题。当我问“我应该”时,我总是厌倦了接近投票,但有 一种 DDD 的做事方式。*

【问题讨论】:

  • 为什么要克隆组织?该问题的答案可能会更多地揭示该服务实际提供的内容。我实际上并不想知道 - 我只是想将您的思维过程提升回领域......
  • @MattDavey 我明白你的意思。但恐怕就这么简单:用户想要“复制”/“复制”现有组织,因为他想创建一个看起来很像以前的组织的新组织。不过,我欢迎您对此提出想法 :)
  • “用户想要“复制”/“复制现有的...”——看起来用户给了你一个解决方案来实现而不是一个问题解决!真正的问题是从头开始创建新组织太耗时吗?在这种情况下,您提供的服务可以加快这一过程吗?
  • @MattDavey 我添加了更多示例,因为我选择的示例毕竟不是很好:/

标签: domain-driven-design ddd-service


【解决方案1】:

这有点基于意见,我想将其添加为评论,但空间不足。

相信在这种情况下,将这些方法组合成一个具有不同构造方法的单独的组织工厂服务是有意义的。

   interface OrganizationFactory{
       Organization createOrganization();
       Organization createOrganizationCopy(Organization organization);
   }

我想这将符合 信息专家 模式和 DRY 原则 - 一个类具有有关特定对象创建的所有信息,我看不出任何原因在不同的地方重复这个逻辑。

不过,有趣的是,在 ddd 中定义了工厂模式

转移创建复杂对象实例的责任和 聚合到一个单独的对象,它本身可能没有 域模型中的责任,但仍然是域的一部分 设计。提供封装所有复杂程序集的接口 并且不需要客户端引用具体的类 被实例化的对象。

“对象”这个词在一般意义上甚至不必是单独的,也可以是工厂方法(我的意思是类的方法和模式factory method) - 后来 Evans 给出了 Brokerage Account 的工厂方法的示例,它创建了 Trade Order 的实例。

这本书提到了GoF工厂模式家族,我不认为有一种特殊的DDD工厂分解方式——重点是创建的对象不是半生不熟的,工厂方法应该添加尽可能少尽可能多的依赖。

更新 DDD 没有附加到任何特定的编程范式,而问题是关于面向对象 分解,所以我不认为 DDD 可以提供任何关于每个对象的方法数量的特别建议。

有些人使用strange rules of thumb,但我相信你可以直接使用High Cohesion principle,并将具有高度相关职责的方法放在一起。因为这是一个 DDD 问题,所以我想它是关于域服务(即不是基础设施服务)。我认为服务应该根据它们在域中的职责进行划分。

更新 2 无论如何 CarService 可以做到 CarService::wash()/ CarService::repaint() / CarService::diagnoseAirConditioningProblems() 但很奇怪 CarWashingService 会做到 CarWashingService::diagnoseAirConditioningProblems() 这就像乔姆斯基的生成语法 -该语言中的某些陈述(句子)有意义,有些则没有。但是如果你的句子包含太多主语(超过 5-7 个),即使是有效的语言句子,也会很难理解。

【讨论】:

  • 我同意你提出的每一个观点,我意识到我的例子并不好。我的问题更笼统:一个服务类应该只实现一个操作,还是让一个服务类聚合多个操作是否可以。新示例:StrategyService::apply()/cancel()StrategyApplicationService::apply()/cancel()?或CarService::wash()CarWashingService::wash()
  • 对象的数量是非常微妙的事情 - C. Alexander 在模式Small parking lots 中引用了其他一些研究表明,仍然可以掌握 5-7 个项目(但不能更多)中的对象作为个人,因此您不必将所有内容都限制为每个类一个方法。 :-)
  • 我已经更新了我的问题的标题:别说方法的数量,什么是服务?它是一个动作,就像现实世界中的“服务”这个词吗?或者它是聚合动作的抽象概念/名称?在之前的评论中查看我的示例
  • 现在重读这篇文章我想知道为什么我没有提到 UBIQUITOUS LANGUAGE 模式并且方法应该符合它。无论如何,有一些关于 SO 的 ddd 专家可能会添加一些更具体的东西。
【解决方案2】:

我认为如果域服务只有一种方法会很好。但我不认为这是一个规则,比如你在域服务或其他东西上不能有多个方法。如果接口只抽象一件事或一种行为,它当然很容易维护,但域服务的粒度完全取决于您的有界上下文。有时我们过于关注低耦合而忽略了高内聚。

【讨论】:

  • +1 这是我试图在我的 cmets 中暗示的。一个好的领域服务可能会抽象出不止一种行为——但它们应该只抽象出一个问题
猜你喜欢
  • 2019-08-23
  • 2021-01-08
  • 1970-01-01
  • 2011-04-19
  • 2016-11-15
  • 1970-01-01
  • 2011-01-20
  • 2015-11-14
  • 1970-01-01
相关资源
最近更新 更多