事件溯源:触发其他事件和事件重建状态

发布于 2024-12-03 13:47:14 字数 213 浏览 1 评论 0原文

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

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

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

I'm struggling to get my head around what should happen when rebuilding the model by replaying events from the EventStore, in particular when events may trigger other events to happen.

For example, a user who has made 10 purchases should be promoted to a preferred customer and receive an email offering them certain promotions.

We clearly don't want the email to be sent every time we rebuild the model for that user, but how do we stop this from happening when we replay our 10th PurchaseMadeEvent ?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

烟燃烟灭 2024-12-10 13:47:14

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

更一般而言 - 事件链的需要经常表明您应该考虑实现 Saga 用于该过程。

Events chaining can be very tricky and easily run out of control, so I'd avoid it as much as possible. For example in the scenario you're describing I'd raise a UserPromotedEvent (maybe even using the PromoteUserCommand), however I wouldn't consider actual/physical sending of an email as part of my domain. Instead I would create additional handler/denormalizer for UserPromotedEvent that would register the need of sending the email with some additional checks quite possibly. After that another process would gather information of not yet processed emails and send them. This approach would mitigate the problems that might occur with not fully accessible/scalable email gateway.

In more general - the need of events chaining very often indicates that you should consider implementing a Saga for the process.

转角预定愛 2024-12-10 13:47:14

您不应该从事件处理程序中引发事件 - 只是不要这样做!您应该使用 sagas 代替。

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

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

You should not raise event from event handler - just don't do it! You should use sagas instead.

In your case, saga subscribes for PurchaseMadeEvent and issues PromoteCustomer COMMAND, which causes to happen CustomerPromoted event. Again, there is another saga that subscribes for CustomerPromoted event and sends SendEmailToPromotedCustomer command. When you are replaying events - just don't subscribe saga for CustomerPromoted event.

This is all about the difference between command and event. It is important to understand it. Events tell what already has happened, commands tell what will happen.

放低过去 2024-12-10 13:47:14

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

例如:

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 相同 - 将执行它的事件处理程序,而不是引发该事件的域方法。

该事件包含更新域模型(并将更新应用到读取模型)所需的所有内容。执行的域方法使您能够引发事件。

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

祝你好运!!

When you replay events, you're not replaying all of the domain logic that went along with generating those events. Usually in your domain method you'll raise an event; the raising of that event then should update the overall state of that domain object.

For example:

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;
  }
}

In this example, when my events are replayed, the OnPurchaseMade event handler will get executed, not the domain object constructor. Same with the PurchaseUpdatedEvent - it's event handler will get executed, not the domain method that raised the event.

The event contains everything that you need to update the domain model (and apply the updates to the read model). The domain methods that get executed get you to the point that an event can be raised.

I hope this helps. Let me know if I need to provide more information.

Good luck!!

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文