领域驱动设计、事件溯源和演化模型

5

Eric Evans在DDD中谈论了模型的演变,因此重构似乎是DDD中必不可少的。当您拥有一个关系型持久化的世界状态时,可以通过迁移更改数据库模式来处理模型更改。

如果使用事件溯源,如何应对模型更改?如果聚合根存在不兼容的更改,会阻止事件重放,是否有某种最佳实践?还是就是不能做到?


1
我前不久在Event Store博客上读到了这篇有趣的文章,也许它会有所帮助:http://geteventstore.com/blog/20130528/why-cant-i-update-an-event/ - JefClaes
@JefClaes 我完全同意Greg的观点,操纵事件历史通常是一个坏主意。但感谢你指出那篇文章,其中有一些好的想法。 - mdo
3个回答

4
如果聚合根发生不兼容的更改,将无法重放事件。在这种情况下,您基本上有两个选择:
1. 对旧事件进行补丁,使它们兼容,并可以从头开始重放事件。好处是您不会丢失历史记录,但缺点是需要花费一些精力来修补旧事件。
2. 在模式更改时拍摄聚合快照/备忘录,并从此点“重新设置”事件流。好处是您不需要花费任何精力(使用事件溯源,您很可能已经有了快照机制)。缺点是您失去了从快照之前重播事件的能力。
作为一个经验法则,我建议默认选择第二个选项,除非您确定需要能够在模式更改之前返回并编辑历史记录。

1
使用快照似乎是朝着一个好的方向前进。如果你还保留重新播放旧事件的选项(版本化的事件类?),我想你应该能够访问整个历史记录...基于我的当前对ES的理解,我更喜欢避免第一种解决方案,因为事件存储被视为只读事件流。最迟,如果你想到了加密链接事件以防止事件链中的操纵,这将变得不可能 :-) --并不是说我现在想这样做。 - mdo

3
我自己没有太多经验。但是我看到一个叫做向上转型(Upcasting)的概念,它最初是面向对象编程中的一个概念,其中:“子类在需要时会自动转换为其超类”。向上转型的概念也可以应用于事件溯源。将事件向上转型意味着将其从原始结构转换为新结构。与面向对象编程的向上转型不同,事件向上转型无法完全自动化,因为旧事件的新结构未知。必须提供手动编写的 Upcaster 来指定如何将旧结构向上转型为新结构。您可以参考 Axon's doc 了解更多细节。

指向这些文档加1,但MikeSW的答案详细解释了。 - mdo

2
事件只是数据传输对象(DTO)。只要事件本身没有变化,模型如何更改都无关紧要,只要你仍然拥有一个对象。如果需要更改事件,则可以使用所需的属性“升级”它。Apply方法将知道该怎么做。如果不了解详细信息,我无法提供具体建议。
如果模型发生了很大变化,基本上现在您有两个聚合根(AR),而不是之前的一个,这意味着您有新的不同聚合,这些聚合将不使用旧事件。基本上,您从旧的AR开始,创建新的AR并生成相应的事件,这些事件将特定于那些AR。因此,在这种情况下,您实际上没有兼容性问题。
与“传统”的面向对象编程(OOP)和关系数据库管理系统(RDBMS)架构相比,使用事件并不是那么直接,但如果您从业务角度思考并将对象视为域概念,则它们更加灵活。更改模型意味着业务概念定义或使用也已更改,因此现在您正在处理不同的(就持久性而言是新的)概念。

+1 这是一个重要的概念。如果域事件更具语义性,那么对域模型(因此也是事件解释方式)的更改不太可能产生连锁反应。 “更改模型意味着业务概念定义或使用也已经发生了变化”,尽管如此,考虑到“向更深层次洞察进行重构”的重要性,我并不完全同意这一点。 Refactoring towards deeper insight - MattDavey
谢谢MikeSW,我在这里找到了一个类似的讨论链接,作者在这里提出了使用Google的Protobuf,就像他在这里所做的一样链接。我考虑到了像删除事件中的一个字段这样的“不兼容”更改。我认为我需要将其考虑在实际应用的上下文中。 - mdo
1
@mdo:Rinat Abdullin的文章“为什么选择事件溯源?”已经迁移到http://abdullin.com/post/event-sourcing-why/,他的“事件溯源-版本控制”也迁移到了http://abdullin.com/post/event-sourcing-versioning/。 - MarnixKlooster ReinstateMonica

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接