Подтвердить что ты не робот

Event Sourcing: события, которые запускают другие и восстанавливают состояние

Я изо всех сил пытаюсь понять, что должно произойти при восстановлении модели путем повторного воспроизведения событий из EventStore, в частности, когда события могут инициировать другие события.

Например, пользователь, совершивший 10 покупок, должен быть повышен до предпочтительного клиента и получать электронное письмо, предлагающее им определенные рекламные акции.

Мы явно не хотим, чтобы письмо отправлялось каждый раз, когда мы перестраиваем модель для этого пользователя, но как мы прекращаем это, когда мы воспроизводим наш 10-й PurchaseMadeEvent?

4b9b3361

Ответ 1

Цепочки событий могут быть очень сложными и легко выходить из-под контроля, поэтому я бы избегал их как можно больше. Например, в описываемом вами сценарии я бы поднял UserPromotedEvent (возможно, даже с помощью PromoteUserCommand), однако я бы не стал рассматривать фактическую/физическую отправку электронной почты как часть моего домена. Вместо этого я создавал бы дополнительный обработчик /denormalizer для UserPromotedEvent, который мог бы зарегистрировать необходимость отправки электронной почты с некоторыми дополнительными проверками вполне возможно. После этого другой процесс будет собирать информацию о еще не обработанных электронных письмах и отправить их. Такой подход позволит устранить проблемы, которые могут возникнуть при недоступном/масштабируемом шлюзе электронной почты.

В более общем смысле - необходимость цепочки событий очень часто указывает на то, что вы должны рассмотреть возможность использования Saga для процесса.

Ответ 2

Вы не должны поднимать событие из обработчика событий - просто не делайте этого! Вместо этого вы должны использовать sagas.

В вашем случае сага подписывается на PurchaseMadeEvent и выдает PromoteCustomer COMMAND, что вызывает событие CustomerPromoted. Опять же, есть еще одна сага, которая подписывается на CustomerPromoted событие и отправляет команду SendEmailToPromotedCustomer. Когда вы воспроизводите события - просто не подписывайте сагу для события CustomerPromoted.

Это все о разнице между командой и событием. Это важно понять. События сообщают, что уже произошло, команды сообщают, что произойдет.

Ответ 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 - it будет выполняться, а не методом домена, который вызвал событие.

Событие содержит все, что вам нужно для обновления модели домена (и применять обновления к модели чтения). Доменные методы, которые выполняются, позволяют понять, что событие может быть поднято.

Надеюсь, это поможет. Дайте мне знать, если мне нужно предоставить дополнительную информацию.

Удачи!