我很难理解通过重播EventStore中的事件来重建模型时会发生什么,特别是当事件可能触发其他事件发生时。
例如,已经进行了10次购买的用户应该被提升为首选客户并收到一封电子邮件,向他们提供某些促销活动。
我们显然不希望每次为该用户重建模型时都会发送电子邮件,但是当我们重播第10个PurchaseMadeEvent
时,我们如何阻止这种情况发生?
答案 0 :(得分:10)
事件链接可能非常棘手并且容易失控,所以我会尽可能地避免它。例如,在您描述的场景中,我会引发UserPromotedEvent
(甚至可能使用PromoteUserCommand
),但我不会考虑将实际/物理发送电子邮件作为我的域的一部分。相反,我会为UserPromotedEvent
创建额外的处理程序/非规范化程序,这可能会注册发送电子邮件的必要性。之后,另一个进程将收集尚未处理的电子邮件的信息并发送它们。这种方法可以缓解不完全可访问/可扩展的电子邮件网关可能出现的问题。
更一般地说 - 事件链接的需要经常表明你应该考虑为这个过程实现Saga。
答案 1 :(得分:6)
你不应该从事件处理程序中引发事件 - 只是不要这样做!您应该使用sagas代替。
在您的情况下,saga订阅PurchaseMadeEvent
并发出PromoteCustomer
COMMAND,这会导致CustomerPromoted
事件发生。同样,还有另一个saga订阅CustomerPromoted
事件并发送SendEmailToPromotedCustomer
命令。当您重播事件时 - 只是不要为CustomerPromoted
事件订阅传奇。
这就是命令和事件之间的区别。理解它很重要。事件告诉已发生的事情,命令告诉将会发生什么。
答案 2 :(得分: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
相同 - 它的事件处理程序将被执行,而不是引发事件的域方法。
该事件包含更新域模型所需的所有内容(并将更新应用于读取模型)。执行的域方法可以让您发现可以引发事件。
我希望这会有所帮助。如果我需要提供更多信息,请告诉我。
祝你好运!!