我目前正在使用Scala和Eventuate,但我认为此问题适用于任何事件源环境:
假设您有以下事件:case class UserAdded(user: User)
,User
为case class User(username:String, birthday: Date)
。
您的应用已运行一段时间,现在您需要收集用户'电话号码。现在我们需要将User
更改为case class User(username:String, birthday: Date, phoneNumber: String)
。
当重播事件时,它会抛出错误,因为过去的事件没有phoneNumber
。您如何在事件源应用程序中解决此问题?
我的第一个想法是为事件存储中的所有事件设置默认值,但我知道当您进行事件采购时事件是不可变的,所以我决定听取更多有经验的用户的意见。
非常感谢任何输入!
答案 0 :(得分:5)
我认为这里有两个概念上的错误。
首先,事件不应“引用”或“包含”域对象。事件是发生的事件的记录,因此它应该明确记录表征它的数据点。在这种情况下 - username
和birthday
:
case class UserAdded(username:String, birthday: Date)
现在,这个事件定义永远不会改变。发生了什么,发生了。没有改写过去。
如果在某个时刻,业务需求发生变化,这意味着从现在开始会发生不同的类型的事件:
case class UserAdded2(username:String, birthday: Date, phoneNumber: String)
两个事件都应该保持完整,两者都应该被处理,处理,等等。直觉可能暗示,第一个事件并非“现在无用”。恰恰相反:第一个事件记录了需求变更之前发生的事情,并且该记录很有价值。
现在,根据您的平台允许的内容(我对Eventuate不熟悉),您可以在技术上将这两个事件实现为具有可选字段的单个类。但即使你这样做,你也应该永远记住这个类代表了两个不同的事件。出于这个原因,我宁愿将它们明确地定义为单独的东西。
答案 1 :(得分:2)
您所描述的内容被称为"版本控制"在事件采购中,尽管事件采购是一项相对较新的技术,但至少有两种解决方案:
您保留旧事件(类和数据)并为class UserAddedV2(username:String, birthday: Date, phoneNumber: String)
等未来事件引入新的事件类型。这样做的缺点是旧类将继续存在,尽管它不再被实例化。
您迁移整个事件流并使用新事件替换旧事件,并使用新字段的默认值。通过这种方式,您可以摆脱旧的事件类。
我强烈推荐Greg Young的book,因为这是我在这个主题上找到的最好的。