14

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

例如,应该将购买了 10 次的用户提升为首选客户,并收到一封电子邮件,向他们提供某些促销活动。

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

4

3 回答 3

11

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

更一般地说 - 事件链接的需求通常表明您应该考虑为流程实现Saga

于 2011-09-06T07:12:00.597 回答
8

您不应该从事件处理程序引发事件 - 只是不要这样做!您应该改用sagas

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

这就是命令和事件之间的区别。理解它很重要。事件说明已经发生的事情,命令说明将要发生的事情。

于 2011-09-06T12:38:03.223 回答
3

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

例如:

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将执行事件处理程序,而不是域对象构造函数。与PurchaseUpdatedEvent- 它的事件处理程序将被执行,而不是引发事件的域方法相同。

该事件包含更新域模型(并将更新应用于读取模型)所需的一切。被执行的领域方法让你达到可以引发事件的地步。

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

祝你好运!!

于 2011-09-05T22:46:09.053 回答