ドメインで起こった状態の変更を記録し、プロセス外依存に伝えるためのパターン ドメインから外部プロセスへの依存を取り除くことができる

例えば、ユーザのメールアドレスが変更されたときに、そのことを外部に伝えるイベントを定義する

public class EmailChangedEvent
{
  public int UserId { get; }
  public string NewEmail { get; }
}

※ ドメインイベントが表現しているのは過去起こった出来事なので、名前も過去形でつける

UserクラスでEmailChangedEventsを持つようにする

public List<EmailChangedEvent> EmailChangedEvents { get; private set; }

public void ChangeEmail(string newEmail, Company company)
{
  Precondition.Requires(CanChangeEmail() == null);
  if (Email == newEmail)
    return;

  UserType newType = company.IsEmailCorporate(newEmail)
    ? UserType.Employee
    : UserType.Customer;

  if (Type != newType)
  {
    int delta = newType == UserType.Employee ? 1 : -1;
    company.ChangeNumberOfEmployees(delta)
  }
  
  Email = newEmail;
  Type = newType;
  EmailChangedEvents.add(
    new EmailChangedEvent(UserId, newEmail); // メールアドレスの変更があったことを伝える
  )
}

コントローラでドメインイベントを送信する

public string ChangeEmail(int userId, string newEmail)
{
  object[] userData = _database.GetUserById(userId);
  User user = UserFactory.Create(userData);

  string error = user.CanChangeEmail();
  if (error != null)
    return error;

  object[] companyData = _database.GetCompany();
  Company company = CompanyFactory.Create(companyData);

  user.ChangeEmail(newEmail, company);

  _database.SaveCompany(company);
  _database.SaveUser(user);

  foreach (var ev in user.EmailChangedEvents)
  {
    _messageBus.SendEmailChangedMessage( // ドメインイベントをメッセージに変換して送信
      ev.UserId, ev.NewEmail);
  }
  
  return "OK";
}

このパターンを採用することで、コントローラから決定の責務を抽出し、ドメインモデルに担わせられる。 ドメインモデルも外部プロセスへ依存しなくてよい。

外部システムとの依存のテストは、ドメインイベントが作られたかどうかだけ検証すればよくなり、コントローラのテストをする必要がない。

[Fact]
public void Changing_email_from_corporate_to_non_corporate()
{
  var company = new Company("mycorp.com", 1);
  var sut = new User(1, "user@mycorp.com", UserType.Employee, false);

  sut.ChangeEmail("new@gmail.com", company);
  company.NumberOfEmployees.Should().Be(0);
  sut.Email.Should().Be("new@gmail.com");
  sut.Type.Should().Be(UserType.Customer);
  sut.EmailChangedEvents.Should().Equal(
    new EmailChangedEvent(1, "new@gmail.com")); // ドメインイベントの要素と数を検証する
}

Execute-CanExecuteパターン