当您以 OO/DDD 风格开发架构并对某些域实体(例如订单实体)进行建模时,您会将与订单相关的整个逻辑放入订单实体中。但是当应用程序变得越来越复杂时,Order 实体收集的逻辑越来越多,这个类变得非常庞大。与贫血模型相比,是的,它显然是一种反模式,但是所有这些庞大的逻辑都在不同的服务中分离。
可以处理巨大的域实体还是我理解错误?
当您以 OO/DDD 风格开发架构并对某些域实体(例如订单实体)进行建模时,您会将与订单相关的整个逻辑放入订单实体中。但是当应用程序变得越来越复杂时,Order 实体收集的逻辑越来越多,这个类变得非常庞大。与贫血模型相比,是的,它显然是一种反模式,但是所有这些庞大的逻辑都在不同的服务中分离。
可以处理巨大的域实体还是我理解错误?
当您尝试创建丰富的领域模型时,请将实体集中在身份和生命周期上,从而尽量避免它们因属性或行为而变得臃肿。
域服务可能是放置行为的地方,但我倾向于看到许多域服务方法的行为可以更好地分配给值对象,因此我不会通过将行为移动到域服务来开始重构。领域服务往往作为直接的外观/适配器在与当前领域模型之外的事物的连接之前工作得最好(即掩盖基础设施问题)。
您也可以将行为放在应用程序服务中,但要问自己该行为是否属于域模型之外。作为一般规则,尝试将应用程序服务更多地集中在跨实体、域服务、存储库的编排式任务上。
当你遇到一个臃肿的实体时,首先要做的是寻找一组有凝聚力的实体属性和相关行为的集合,并通过将这些隐含的概念提取到值对象中来使它们变得明确。然后实体可以将其行为委托给这些值对象。
由于我们都倾向于对实体更满意,因此请尝试更偏向于值对象,以便您获得值对象提供的不变性、封装性和可组合性的好处——使您走向更灵活的设计。
值对象使您能够将更具功能性的样式(例如,无副作用的函数)合并到您的域模型中,从而使您的实体免于处理将复杂行为添加到管理身份和生命周期的负担中的复杂性。有关更多详细信息,请参阅 Eric Evan 的http://domainlanguage.com/ddd/patterns/和蓝皮书中实体和值对象的模式摘要。
当您以 OO/DDD 风格开发架构并对某些域实体(例如订单实体)进行建模时,您会将与订单相关的整个逻辑放入订单实体中。但是当应用程序变得越来越复杂时,Order 实体收集的逻辑越来越多,这个类变得非常庞大。
有变大趋势的类,往往是职责重叠的类。Order是一个典型的类示例,它可以具有多种职责,并且可以在您的应用程序中扮演不同的角色。
鉴于订单出现的上下文,它可能是一个具有可变状态的实体(即,如果您正在管理订单的商业条件,则在协商阶段)但如果您的应用程序正在管理物流,则订单可能会扮演不同的角色:不可变的值对象可能是逻辑上下文中的最佳实现。
与贫血模型相比,是的,它显然是一种反模式,但是所有这些庞大的逻辑都在不同的服务中分离。
……分离是件好事。:-)
我有一种感觉,原始模型可能是以数据为中心的,并且服务于不同目的(订单创建、支付、订单履行、订单交付)的数据堆积在同一个容器(订单类)中。不能从这里说出来,但这是一种非常常见的模式。并非所有这些数据都能同时用于相同目的。
通常,像您所描述的那样臃肿的类是Bounded Contexts之间缺少分离的气味,和/或同一有界上下文中不完整的Aggregate分离。我会看看:
并尝试相应地重新定义聚合边界。还有:
当涉及到发现所涉及的上下文时。
在大型应用程序中,您可能有多个模型,因此导致单个域概念的多个表示,至少对于扮演多种角色的概念而言。
这是对保罗方法的补充。
在 DDD 中使用服务很好。您通常会在域、应用程序或基础设施层看到服务。
Eric 在他的书中使用这些准则来确定何时使用服务: