l

2022年7月2日 星期六

事件溯源(5):如何儲存事件型別

June 30 18:41~19:43

▲圖1:每一筆領域事件都需要儲存它的型別(Type)資料

 

前言

無論是Event Sourcing或是Outbox,領域事件必須被「序列化(serialize)」之後方可儲存到資料庫,從資料庫讀出則是經過「反序列化(deserialize)」之後變成領域事件物件。這個序列化、反序列化的過程,必須知道領域物件的型別才可以完成。因此,領域物件儲存到Event Store必須記錄事件型別,如圖1所示。今天要討論的問題是:「在Event Store中要如何記錄領域物件型別?」

***

紀錄Package Name + Class Name

這個問題乍看之下好像很簡單,啊不就儲存Domain Event的完整型別就好了?例如儲存TagEvents.TagCreated這個領域事件,只要呼叫:

TagEvents.TagCreated.class.getCanonicalName()

把下列回傳值儲存到資料庫的event type欄位就好了。

ntut.csie.sslab.ezkanban.kanban.tag.entity.TagEvents.TagCreated

反序列化的時候就用 class.forName() 就可以產生領域事件物件。

如果你永遠都不會修改領域事件或是package名稱,也不會把領域事件移動到不同的package,那麼直接儲存領域事件的完整型別是沒問題的。但是,只要將領域事件改名或是移動到不同的package,那麼讀取儲存在Event Store的舊領域事件就會發生 class not found例外。

看到這裡你可能會想:「我去更新資料庫中event type欄位的資料不就好了?」但是Event Sourcing系統有一個特性,就是基本上開發人員不應該去修改已經存在的領域事件,一般而言不建議用這種方式。

 

***

型別對照(Type Mapping)

一般的作法會建議採用型別對照的方式,序列化的時候針對每一種領域事件儲存一個唯一且固定不變的字串到資料庫中,反序列化則是依據這個固定的字串去「查表」,找出相對應的領域事件型別。

實作方法很簡單,首先宣告一個DomainEventTypeMapper介面,如圖2。

 

▲圖2:DomainEventTypeMapper介面

 

接下來針對每一個Aggregate的所有領域事件,寫一個DomainEventTypeMapper。圖3是給Tag使用的DomainEventTypeMapper,針對TageCreated、TagRenamed、TagColorChanged、TagDeleted這四個領域物件,宣告四個唯一且不變的字串做為寫入到Event Store的事件型別(第55~58行)。

第60行~66行設定這四個領域物件型別字串分別代表哪一個真正的Java領域物件類別,也就是建立型別對照所需要的表格。


▲圖3:Tga的DomainEventTypeMapper

 

ezKanban在序列化、反序列化的過程會呼叫圖4中的DomainEventMapper類別,第28行toMappintType()方法將領域事件轉成固定的型別字串,第38行則是在反序列化的時候透過領域物件型別字串找到真正的領域物件。


▲圖4:DomainEventMapper類別

***

下集預告

Event Sourcing對於寫入操作非常方便,不需要做OR-Mapping只需寫入領域事件。但是,要查詢資料的時候怎麼辦?例如,TagRepository只可以依據tagId找到單一個Tag,如果要找出某一個Board裡面的全部Tag,要怎麼辦?下一集討論這個問題。

***

友藏內心獨白:這一集簡單很多。

沒有留言:

張貼留言