我正在研究一个领域驱动的设计实现,在这个实现中,我们有一些操作在相同的聚合中,需要与其他操作一起进行。这两种操作是互不相关的。
这是一个示例代码。
注意,此代码仅用于说明性目的,而不是实际示例。
public ProcessOrderCommandHandler : IHandler<ProcessOrderCommand>
{
public async Task Handle(ProcessOrderCommand orderCommand)
{
var order = _repository.LoadOrder(orderCommand.Id);
// Operation - 1
order.AddToCart(orderCommand.Item);
// Operation - 2
order.ProcessOrder();
}
}
public class Order : Aggregate
{
public void AddToCart(Item item)
{ ... }
public void ProcessOrder()
{ ... }
}ProcessOrder和AddToCart操作是不相关的,我有许多这样的CommandHandlers,它们彼此独立,但仍然需要联合调用。
我认为解决这一问题有三种选择:
CommandHandler中调用多个域操作。同样,如果这是正确的方式,也不是100%的方便,因为我需要在所有相关操作中这样做。
我觉得这种方法是最干净的,因为它有助于保持关注点的分离。但是,在这里,我通过域-事件来处理同一个聚合中的两个域操作,而这并不是域事件通常的使用方式。根据来自Microsoft的域-事件定义:
使用域事件显式地实现域内更改的副作用。 换句话说,使用DDD,使用域事件显式地实现跨多个聚合的副作用。
问:在领域驱动设计中,选项3是一个可接受的解决方案吗?
发布于 2020-07-17 22:03:45
ProcessOrder和AddToCart操作是不相关的,我有许多这样的CommandHandlers,它们彼此独立,但仍然需要联合调用。
在直接回答你的问题之前,我建议重新评估这一声明。如果操作不相关,为什么它们属于同一个集合?另外,这些CommandHandlers是否运行在相同的集合上?
简而言之,您确定您的集合是正确的吗?他们应该有一个单一和明确的责任。如果有不相关的操作或太多的操作,它们可能承担多个职责。
现在,关于你的三个选择。在我看来,CommandHandlers被称为清洁架构中的用例。在您的场景中,用例基本上是从DB加载聚合,调用业务操作,并将更新的聚合存储回DB (加上潜在的发布事件、与第三方的对话等)。因此,如果您这样认为,您的用例需要在同一个聚合中调用2个业务操作这一事实并不是一个问题,因为这就是您的用例所指定的。
如果这两个操作没有意义,您可以将这两个操作合并为一个操作。
如果我们假设聚合是正确的,那么选项3是我唯一不会去看的。在我看来,这将是滥用一个模式的东西,它是不打算使用。
但是,如果您认为,实际上,这些操作属于不同的聚合,那么使用事件肯定是最好的选择。
考虑以下要求:
在这个场景中,您显然有两个聚合(购物车和船运)。添加到Cart和重新计算航运成本是两个独立的操作,它们与事件协调更有意义,并与业务规范保持一致(“当X发生时.”)。
现在考虑上一个示例的另一个用例:
现在,更新航运地址和重新计算运输成本是在同一个聚合上的两个操作,但在这种情况下,将它们与用例分开调用是没有意义的,因为更改地址而不重新计算成本将使聚合处于不一致的状态,因此聚合本身可以在地址更改后立即自动重新计算成本。
发布于 2020-07-19 07:34:56
ProcessOrder()方法是否只反映在同一个聚合上执行的聚合内部的更改,以便在向购物车中添加某些项时作出反应?如果是,我将把对该操作的调用移到AddToCart()方法中。如果无论何时添加一个项目,都必须在同一个聚合中发生一些业务不变,那么为什么要把这个责任放在应用程序层呢?这将只允许您的业务逻辑泄漏到域层之外。如果您想要使用基于事件的方法在相同的聚合上内聚地实现这些操作,这是您选择的设计决策,可能更适合,也可能不适合。但是,在添加了项之后,这个处理就不再是应用层的责任了。
但是如果ProcessOrder()方法代表了用户在购物车中添加了一些东西(比如提交订单)之后的下一个步骤,那么您应该问问自己,我与域模型的交互是否太基于 CRUD __?
因此,根据我的经验,DDD通常更适合,或者说更容易适用于基于任务的用户接口。这意味着客户端(例如Web浏览器)通过执行较小的、定义良好的任务与系统进行交互,而不是按照顺序发送更多的数据,而不是转换成在域模型上执行的几个任务(此处是聚合)。
因此,在您的示例中,如果ProcessOrder()的意思是“提交订单”,我将亲自与后端进行两次交互。一个用于向购物车添加一些东西--用例A,另一个用于提交订单--用例B。
对我来说,在这种情况下,从客户的角度来看,这也是比较自然的,因为我希望在不同的步骤中执行这些任务。此外,如果考虑这种方法,则不必处理事件,特别是在相同聚合上操作时。
发布于 2020-07-19 08:36:31
如果您的领域没有太多的上下文,很难判断,但这是您应该从您的产品专家那里得到的答案。
似乎您有一个订单聚合,它公开了一个在购物车上执行操作的公共接口。
(这是对任何商店实际发生的事情的描述,以帮助说明我的想法)
在这一过程中,我们有:
同样,这是我基于您的描述所做的假设,但我使用它来表明可以从业务流程中实际发生的事情中提取概念,并且领域专家应该帮助您正确地处理这些概念。
https://stackoverflow.com/questions/62961542
复制相似问题