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日在倫敦舉行。
查看英文原文:Versioning of Events in Event Sourced Systems