Domain driven design 事件来源:触发其他人的事件&;重建国家

Domain driven design 事件来源:触发其他人的事件&;重建国家,domain-driven-design,cqrs,event-sourcing,Domain Driven Design,Cqrs,Event Sourcing,通过重播EventStore中的事件,特别是当事件可能触发其他事件发生时,我正在努力弄清楚在重建模型时应该发生什么 例如,购买了10件商品的用户应被提升为首选客户,并收到一封向其提供特定促销的电子邮件 显然,我们不希望在每次为该用户重建模型时都发送电子邮件,但当我们重播第10次PurchaseMadeEvent时,如何阻止这种情况发生?当您重播事件时,您并不是在重播生成这些事件的所有域逻辑。通常在域方法中,您将引发一个事件;然后,该事件的引发应该会更新该域对象的整体状态 例如: public c

通过重播EventStore中的事件,特别是当事件可能触发其他事件发生时,我正在努力弄清楚在重建模型时应该发生什么

例如,购买了10件商品的用户应被提升为首选客户,并收到一封向其提供特定促销的电子邮件


显然,我们不希望在每次为该用户重建模型时都发送电子邮件,但当我们重播第10次PurchaseMadeEvent时,如何阻止这种情况发生?

当您重播事件时,您并不是在重播生成这些事件的所有域逻辑。通常在域方法中,您将引发一个事件;然后,该事件的引发应该会更新该域对象的整体状态

例如:

public class Purchase {
  private int _id;
  private string _name;
  private string _address;
  private double _amount;

  public Purchase(int id, string name, string address) {
    //do some business rule checking to determine if event is raised

    //perhaps send an email or do some logging
    //etc.
    if (should_i_raise_event) {
      ApplyEvent(new PurchaseMadeEvent() {
        ID = id,
        Name = name,
        Address = address
      });
    } 
  }

  public UpdatePurchase(int id, double amount) {
    //more checking to see if event is to be raised
    if (should_i_raise_event) {
      ApplyEvent(new PurchaseUpdatedEvent() {
        ID = id,
        Amount = amount
      });
    }
  }

  protected void OnPurchaseMade(PurchaseMadeEvent e){
    _id = e.ID;
    _name = e.Name;
    _address = e.Address;
  }

  protected void OnPurchaseUpdated(PurchaseUpdatedEvent e){
    _id = e.ID;
    _amount = e.Amount;
  }
}
在本例中,当我的事件被重播时,将执行
OnPurchaseMade
事件处理程序,而不是域对象构造函数。与
purchaseUpdateEvent
相同-将执行它的事件处理程序,而不是引发事件的域方法

该事件包含更新域模型(并将更新应用于读取模型)所需的所有内容。执行的域方法会使您达到可以引发事件的程度

我希望这有帮助。如果我需要提供更多信息,请告诉我


祝你好运

事件链接非常棘手,很容易失控,所以我会尽量避免。例如,在你描述的场景中,我会提出一个<>代码> UsServestDeunt/<代码>(也许甚至使用<代码> PROTETUSER命令),但是我不会考虑电子邮件的实际/物理发送作为我的域的一部分。相反,我将为
UserPromotedEvent
创建额外的处理程序/反规范化程序,它很可能会注册发送电子邮件的需要,并进行一些额外的检查。之后,另一个过程将收集尚未处理的电子邮件的信息并发送它们。这种方法可以缓解不完全可访问/可扩展电子邮件网关可能出现的问题


更普遍的是,事件链接的需求经常表明你应该考虑为该过程实现一个。

不应该从事件处理程序中引发事件——只是不要这样做!你应该改用

在您的情况下,saga订阅
PurchaseMadeEvent
并发出
PromoteCustomer
命令,这导致发生
CustomerPromoted
事件。同样,还有另一个传奇故事订阅
CustomerPromoted
事件并发送
sendmailtopromotedcustomer
命令。当您重播事件时-不要订阅
CustomerPromoted
事件的传奇


这就是命令和事件之间的区别。理解它很重要。事件告诉我们已经发生了什么,命令告诉我们将会发生什么。

我意识到这是一个非常古老的答案,但是,传奇如何知道在收到
PurchaseMadeEvent
时发出
PromoteCustomer
命令?这依赖于(根据OP的描述)客户已经购买了10次,这是传奇不应该包含的领域逻辑。也许总是启动一个
TestToPromoteCustomer
,让聚合人员进行检查以实际执行促销?但这感觉有点笨拙。。谢谢你的见解是的,你是对的,saga应该发送TestToPromote客户命令。我认为为了可维护性而牺牲纯度是完全可以的。@Geert Jan不需要这样做。聚合根用户决定拒绝或处理该命令。