Jan. 10 22:29~23:59
▲圖1:ezKanban三個bounded contexts之間訊息流動方向
領域事件造成的原始碼相依性
上一集〈領域驅動設計學習筆記(14):依據領域事件流動方向調整Bounded Context的依賴關係〉提到依據領域事件流動方向將ezKanban的bounded context依賴方向由原本的:
- Team—>Account
- Team—>Board
調整成如圖1所示的:
- Team—>Account
- Board—>Team
請參考圖2,以UserRegistered領域事件為例,當使用者成功註冊之後,系統會自動幫該使用者建立一個預設的Team(團隊),當使用者登入系統之後,就可以在這個預設的團隊裡面新增Board(看板)或Project(專案)。
▲圖2:UserRegistered領域事件由Account bounded context傳遞到Team bounded context
ezKanban雖然切成三個bounded context,但目前採用Monolithic(單體)佈署方式,因此三個bounded context透過一個共享的in memory event bus來發送與接收領域事件。圖3展示位於Team bounded context的event handler—NotifyTeam程式,它監聽UserRegistered領域事件,當該事件發生時,呼叫CreateTeamUseCase幫剛註冊的使用者新增一個預設的團隊。
▲圖3:NotifyTeam事件處理程式
看到這裡鄉民們應該就很清楚,基本上Account與Team是兩個獨立的bounded context,彼此之間只有在runtime(程式執行期間)透過領域事件產生相依,compile time(編譯期間)原則上是不需要產生原始碼相依性。但因為一開始Teddy與ezKanban團隊偷懶的關係,讓Team直接去聽Account的UserRegistered領域事件,因此Team需要將UserRegistered類別import進來,所以產生Team對Account bounded context的原始碼依賴。
***
拿掉原始碼依賴
拿掉Team對Account bounded context的原始碼依賴也不會很麻煩:
- 設計一個RemoteDomainEvent類別,用來代表跨bounded context的領域事件,並將它放在所有專案都會參考的共用模組裡面。
- 不要直接把UserRegistered類別傳給Team,而是把UserRegistered「序列化」(例如轉成JSON格式)之後用RemoteDomainEvent將它包起來再傳出。
- Account不再監聽UserRegistered領域事件,改監聽RemoteDomainEvent。當收到RemoteDomainEvent之後,將其內容打開判斷是否為UserRegistered領域事件,如果是再執行原本的事件處理程序。
請參考圖4,NotifyAccount是位於Account bounded context的事件處理程式,它監聽自己的UserRegistered領域事件,並將其轉成RemoteDomainEvent。
▲圖4:NotifyAccount事件處理程式
圖5展示新的NotifyTeam事件處理程式,它改成監聽RemoteDomainEvent,並依據事件的tag與事件名稱將其轉交給合適的事件處理程式來處理。
▲圖5:新的NotifyTeam事件處理程式
將原始碼相依性徹底拿掉,從佈署的角度來看,要從Monolithic(單體)轉成Microservices(微服務)對系統架構來講要改動的幅度也就比較小,增加佈署方式的選擇彈性。
***
依賴依然存在
最後提醒一點,Account與Team這兩個bounded context,從Context Map的角度來看,應屬於Customer/Supplier的上下游關係,所以兩者之間還是有依賴關係,只不過這個依賴關係由原始碼依賴改成符合真實世界狀況的執行期間的訊息依賴。如果Account的UserRegistered領域事件介面改變,還是有可能會導致Team發生執行期間錯誤(runtime exception)。
***
友藏內心獨白:動態比較有彈性也比較容易出錯XD。
沒有留言:
張貼留言