4

我对在基于六边形架构的应用程序中处理域事件的位置感到困惑。我说的是有界上下文内部域事件,而不是上下文集成/应用程序/公共事件。

背景

据我了解,应用程序逻辑(即用例逻辑、工作流逻辑、与基础设施的交互等)是命令处理程序所属的地方,因为它们特定于某个应用程序设计和/或 UI 设计。然后,命令处理程序调用所有域逻辑所在的域层(域服务、聚合、域事件)。领域层应该独立于特定的应用程序工作流和/或 UI 设计。

在许多资源(博客、书籍)中,我发现人们在应用层实现域事件处理程序,类似于命令处理程序。这是因为域事件的处理应该在它自己的事务中完成。由于它可能会影响其他聚合,因此必须首先通过基础架构加载这些聚合。然而,关键点是:领域事件被撕裂并变成一系列对聚合的方法调用。这个重要的翻译只存在于应用层。

问题

我认为关于哪些领域事件会对其他聚合产生什么影响的知识是领域知识本身的一个组成部分。如果我要删除除我的领域层之外的所有内容,那么这些知识不应该保留在某个地方吗?在我看来,我们应该将领域事件处理程序直接放在领域层本身:

  • 它们可以是接收域事件和可能受其影响的聚合的域服务,并将域事件转换为一个或多个方法调用。

  • 它们可以是聚合本身的方法,直接使用整个域事件(即签名包含域事件类型)并使用它做任何他们想做的事情。

当然,为了加载受影响的聚合,我们仍然需要在应用层有一个对应的处理程序。这个处理程序只启动一个新事务,加载感兴趣的聚合并调用到域层。

由于我从未在任何地方看到过这个,我想知道我是否对 DDD、域事件或应用层和域层之间的区别有什么问题。

编辑:示例

让我们从这个常用的方法开始:

// in application layer service (called by adapter)
public void HandleDomainEvent(OrderCreatedDomainEvent event) {
    var restaurant = this.restaurantRepository.getByOrderKind(event.kind);
    restaurant.prepareMeal(); // Translate the event into a (very different) command - I consider this important business knowledge that now is only in the application layer.
    this.mailService.notifyStakeholders();
}

而这个呢?

// in application layer service (called by adapter)
public void HandleDomainEvent(OrderCreatedDomainEvent event) {
    var restaurant = this.restaurantRepository.getByOrderKind(event.kind);
    this.restaurantDomainService.HandleDomainEvent(event, restaurant);
    this.mailService.notifyStakeholders();
}

// in domain layer handler (called by above)
public void HandleDomainEvent(OrderCreatedDomainEvent event, Restaurant restaurant) {
    restaurant.prepareMeal(); // Now this translation knowledge (call it policy) is preserved in only the domain layer.
}
4

3 回答 3

4

大多数偶数处理程序类的问题在于,它们通常与特定的消息传递技术相关联,因此通常放置在基础设施层中。

但是,没有什么能阻止您编写与技术无关的处理程序并使用分派给它们的技术感知适配器。

例如,在我构建的一个应用程序中,我有一个Action Required Policy的概念。每当满足/不满足策略规则时,该策略就会将给定工作项的分配/取消分配到一个特殊的工作负载桶。在许多情况下必须重新评估该策略,例如何时将文档附加到工作项、分配工作项、授予外部状态标志等。

我最终ActionRequiredPolicy在域中创建了一个类,该类具有事件处理方法,例如void when(CaseAssigned event),我在基础设施层中有一个偶数处理程序,它只是通知策略。

我认为人们将它们放在infrastructureorapplication层中的另一个原因是,策略通常通过触发新命令来对事件做出反应。有时这种方法感觉很自然,但有时您想明确表示必须为响应事件而发生动作,否则无法发生:将事件转换为命令会使其不那么明确。

这是我提出的与此相关的一个较旧的问题。

于 2021-04-21T04:04:11.787 回答
2

您的描述听起来很像事件溯源。

如果事件溯源(聚合的状态完全来自域事件),那么事件处理程序位于域层中,实际上一般趋势是让端口/适配器/反腐败层发出命令;然后(如果需要)聚合的命令处理程序使用事件处理程序来派生聚合的状态,然后基于状态和命令发出持久的事件,以便事件处理程序可以派生下一个状态。请注意,这里的事件处理程序肯定属于域层,命令处理程序也可能属于。

在我看来,更一般的事件驱动方法倾向于隐含地利用这样一个事实,即一方的事件通常是另一方的命令。

值得注意的是,事件在某种意义上通常只是对聚合的具体方法调用。

于 2021-04-18T21:06:14.590 回答
1

我遵循以下策略来管理域事件:

首先,最好将它们保存在事件存储中,这样您就可以在触发事件的事实(例如,创建用户)和它触发的操作(例如,向用户)。

假设我们有一个命令总线:

  • 我在它周围放置了一个装饰器,它保留了命令生成的事件。
  • 工作人员处理事件存储并在有界上下文 (BC) 之外发布事件。
  • 其他对该事件感兴趣的 BC(或发布它的相同),订阅它。事件处理程序就像命令处理程序一样,属于应用层。

如果使用六边形架构,则六边形分为应用层和域。

于 2021-04-23T06:48:26.827 回答