ドメインで起こった状態の変更を記録し、プロセス外依存に伝えるためのパターン ドメインから外部プロセスへの依存を取り除くことができる
例えば、ユーザのメールアドレスが変更されたときに、そのことを外部に伝えるイベントを定義する
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")); // ドメインイベントの要素と数を検証する
}