我正在尝试实现纯事件源服务,以查看在哪里出现问题。现在,我发现了一个到目前为止无法解决的问题,因此我想就此进行讨论。
给出以下汇总:
class User
{
public Guid Id { get; private set; }
public string Name { get; private set; }
public void Apply(UserNameChangedEvent domainEvent)
{
Name = domainEvent.NewName;
}
public void Apply(UserCreatedEvent domainEvent)
{
Name = domainEvent.Name;
Id = domainEvent.Id;
}
}
和那些域事件
class UserCreatedEvent
{
public string NewName { get; }
public Guid Id { get; }
public UserCreatedEvent(string newName, Guid id)
{
NewName = newName;
Id = id;
}
}
class UserNameChangedEvent
{
public string NewName { get; }
public UserNameChangedEvent(string newName)
{
NewName = newName;
}
}
可以说我创建了一个用户,然后将其名称更改为“ Peter”,然后在我的EventStore中保留了一个UserCretedEvent
和一个UserChangedNameEvent
。现在,企业表示不再可以更改名称,因此我将删除类UserChangedNameEvent
和处理该名称的函数。但是现在我有一个问题,就是我无法以正确的状态(名称为“ Peter”)重新创建聚合。
当然,我可以开始研究,并将功能和类标记为已弃用,因此我可以继续使用它,但是此后我可能会遇到很多事件类,这将是一个噩梦,需要跟踪。我还听说您可能会创建一个新事件,该事件会保留域中的更改,但是对我来说,这似乎很棘手,而且风格也不是很好,因为从我的角度来看,这不是域事件。
问题是,我该如何最好地应对这种变化?
编辑:只是为了澄清:我不想删除任何事件,只删除我使用它的类和函数,因为现在的要求有所不同。
答案 0 :(得分:1)
格雷格·杨(Greg Young)的事件模式变更相关问题的资源为Versioning in an Event Sourced System。
问题是,我该如何最好地应对这种变化?
这取决于您要解决的实际问题。
如果要求将来不允许用户更改其名称,则可以取消域模型中创建 new UserNameChangedEvent
的逻辑,但请假在事件确实出现的正确处理之后。
如果要求忽略对用户名的更改,那么您也可以使用Apply(UserNameChanged)
处理程序并将其转换为NoOp,就像处理任何其他无法识别的事件一样。
如果要求删除有关名称更改的信息,则可以将事件存储迁移到新的架构,该架构不再包含UserNameChanged
事件。
如果将状态存储在RDBMS中,则可能会帮助您仔细考虑如何解决问题:是否足以忽略“用户名”列?您需要删除该列吗?您是否需要(以某种方式)将列中的值恢复为先前写入的值?
在传统数据库中知道与您要在事件存储中解决的问题类似的问题,应该有助于确定适当的解决方案。
也:请注意您的域模型是否是需要更改的数据的记录系统,或者您是否正在缓存由不同机构发布的信息的表示形式。
答案 1 :(得分:1)
事件捕获了有关系统的事实。如果某个时候更改了用户名,那是事实。将来的业务规则更改不会影响过去的事实。 因此,您不应该删除UserNameChanged事件,所有相关的处理程序,事件都在那里,并且您不应该重写过去的历史记录。
在CQRS应用程序中,事件由命令处理程序生成。因此,这是您指定业务需求的地方。 “现在企业说不再可以更改名称了”,这意味着ChangeName命令不再可用:您可以简单地删除它,或者抛出一个错误消息,说您不能再更改名称。