Domain driven design 如何在DDD中使用大型域类?

Domain driven design 如何在DDD中使用大型域类?,domain-driven-design,Domain Driven Design,当您以OO/DDD风格开发架构并对某些领域实体(如订单实体)建模时,您将把与订单相关的整个逻辑放入订单实体中。 但当应用程序变得更加复杂时,Order entity收集了越来越多的逻辑,这个类变得非常庞大。 与贫血模型相比,是的,这显然是一种反模式,但所有这些巨大的逻辑在不同的服务中是分离的 处理大型域实体可以吗?或者我理解有什么错误?在DDD中使用服务也可以。您通常会在域、应用程序或基础架构层看到服务 Eric在他的书中使用这些指导原则来确定何时使用服务: 该操作涉及的域概念不是实体或值对象

当您以OO/DDD风格开发架构并对某些领域实体(如订单实体)建模时,您将把与订单相关的整个逻辑放入订单实体中。 但当应用程序变得更加复杂时,Order entity收集了越来越多的逻辑,这个类变得非常庞大。 与贫血模型相比,是的,这显然是一种反模式,但所有这些巨大的逻辑在不同的服务中是分离的


处理大型域实体可以吗?或者我理解有什么错误?

在DDD中使用服务也可以。您通常会在域、应用程序或基础架构层看到服务

Eric在他的书中使用这些指导原则来确定何时使用服务:

  • 该操作涉及的域概念不是实体或值对象的自然部分
  • 接口是根据域模型中的其他元素定义的
  • 该操作是无状态的

当您试图创建丰富的域模型时,请将实体的重点放在标识和生命周期上,从而避免它们因属性或行为而变得臃肿

域服务可能是放置行为的地方,但我倾向于看到许多域服务方法的行为可以更好地分配给值对象,因此我不会通过将行为移动到域服务来开始重构。域服务往往在连接到当前域模型之外的事物(即屏蔽基础设施问题)之前,作为直截了当的外观/适配器工作得最好

您还可以将行为放入应用程序服务中,但要问问自己该行为是否属于域模型之外。作为一般规则,尝试将应用程序服务更多地集中于跨实体、域服务和存储库的编排样式的任务

当您遇到一个膨胀的实体时,首先要做的事情是寻找一组内聚的实体属性和相关行为,并通过将这些隐式概念提取到值对象中使其显式化。然后,实体可以将其行为委托给这些值对象

因为我们都倾向于更舒适地使用实体,所以尽量偏向于价值对象,这样您就可以获得价值对象提供的不变性、封装性和可组合性的好处,从而使您朝着更灵活的设计迈进

价值对象使您能够将更具功能性的样式(例如,无副作用的功能)合并到域模型中,从而使您的实体不再需要处理将复杂行为添加到管理身份和生命周期负担中的复杂性。有关更多详细信息,请参阅Eric Evan和蓝皮书中实体和值对象的模式摘要

当您以OO/DDD风格和建模开发架构时 某些域实体,例如,您正在放置整个逻辑的订单实体 将订单关联到订单实体。但是当应用程序变得 更复杂的是,订单实体收集了越来越多的逻辑,这 班级变得非常庞大

有变大趋势的类通常是职责重叠的类Order是一个典型的类示例,该类可以有多种职责,并且可以在应用程序中扮演不同的角色

鉴于订单出现的上下文,它可能是一个状态可变的实体(即,如果您在谈判阶段管理订单的商业条件),但如果您的应用程序正在管理物流,一个顺序可能扮演不同的角色:一个不可变的值对象可能是逻辑上下文中最好的实现

与贫血模型相比,是的 显然是一种反模式,但所有这些巨大的逻辑在 不同的服务

…分离是件好事。:-)

我有一种感觉,原来的模型可能是以数据为中心的,用于不同目的(订单创建、付款、订单履行、订单交付)的数据都堆积在同一个容器(订单类)中。从这里说不上来,但这是一种非常常见的模式。并非所有这些数据同时用于相同目的

通常,像您所描述的那样臃肿的类会感觉到有界上下文之间缺少分离,和/或同一有界上下文中的聚合分离不完整。我想看看:

  • 一起改变的事物
  • 因为同样的原因而改变的事物
  • 实现行为所需的信息
并尝试相应地重新定义聚合边界。以及:

  • 申请的不同目的
  • 不同的利益相关者
  • 不同的隐式模型/语言
当涉及到发现所涉及的上下文时

在大型应用程序中,您可能有多个模型,从而导致单个域概念的多个表示,至少对于扮演许多角色的概念是这样


这是对Paul方法的补充。

域实体可以与基础架构层一起工作吗?例如,在哪里可以找到发送订单并更改其内部状态的方法SendByEmail()?通常最好将责任分解,因此
SendEmail()
方法可能是域层中的
OrderServices
类的责任,应该只发送一封电子邮件,而不发送其他内容。如果您还需要将订单标记为已完成,这可能是对
CompleteOrder()
的调用,可以将其保留在您的订单实体上。所有这些都是特定于您的应用程序的,以及当您在整个模块中运行它们时的感受。然后,您可以创建一个更高级别(更抽象的)
应用程序服务
,比如
ProcessOrder()
,它可以按顺序完成许多事情