Greg Young 在今年 DDD eXchange 会议的演讲中指出,在事件溯源系统(event sourced system) 中,其中的一项挑战就在于软件可能已经经历了许多的变更,但是数年前放到事件存储中的事件现在必须依然能够读取。系统的有些消费者,比如预测或其他的系统,必须要能够同时处理老版本和新版本的事件。
如果系统能够废弃,然后更新并重新运行,事件的版本化相对会比较简单。Young 是 CQRS 和事件溯源的权威,同时还是 Event Store 的首席架构师,对他来说,真正的挑战在于系统无法废弃,软件的两个版本必须要同时运行。
版本化事件的通用规则就是添加新内容并不会导致版本冲突。因此,添加新版本的事件并不是问题,只要我们不破坏新版本事件的定义即可,老版本的相同事件必须能够转换为新版本的事件。如果无法实现的话,那么这就是一个新的事件。按照上述的规则,当从存储中读取老版本的事件时,在处理之前,它们可以首先转换或向上转型为最新的版本。这意味着事件处理器(handler)只需要知道如何处理最新的版本即可,如果要创建很多新版本的事件的话,那么这会是一项重要的优势。如果新版本的事件新增了某项属性,在转换老版本的事件时,我们可以采用在数据库中添加新的非空列时相同的原则,那就是使用默认值。
对于基于类型系统的事件来说,我们使用的是强模式。如果无法通过序列化器得到任何支持的话,那么只能通过类型或模式的精确匹配来读取事件了。Young 指出这种方式只适用于我们能够废弃掉原系统并进行升级的情况,否则的话,消费者可能会读取到它们并不认识的新版本事件,因此无法对它们进行反序列化。因此,他是不支持这种方式的。
在使用 JSON 或 XML 格式来描述事件的时候,我们使用的是一种弱模式,我们不是反序列化,而是使用映射的方式将 JSON 数据转换为事件对象。如果两者具有相同名称的属性的话,那么我们就将值复制到事件中。如果 JSON 数据中存在的值,在事件中没有对应的属性的话,我们只需舍弃对应的值即可。如果事件中的某个属性没有在 JSON 数据中找到值的话,那么就使用默认值。为了让这种机制能够正常运行,我们不允许重命名任何属性,我们也不会修改属性的语义。这种技术的优势在于我们不需要创建事件的多个新版本,只需要使用最新的版本就可以。对于 Young 来说,这项技术更合适,因为老版本的软件能够读取新的事件。
另外一个可选方案,与弱模式有关,它是一种混合模式,对于事件至关重要的属性是必选的,其他的都是可选的,比如订单的标识符对于该订单相关的事件来说就是必选的。
其他复杂的问题包括发布了本不应该发布的事件以及聚集边界(aggregate boundaries)出现错误的情况。升级已有的事件可能会导致很大的问题,Young 反对这样做。他推荐使用流来进行操纵,通过读取流,我们可以进行任何类型的转换,进行必要的转换然后写入到新的流中。旧的流随后可以删除。其他的样例包括流的连接(joining)和切分(splitting)。
关于事件的版本化,Young 正在编写一本书,这本书讨论了如何应对长期的版本化问题。
明年的DDD eXchange Conference 将会在2018 年4 月26-27 日在伦敦举行。
评论