Dec. 18 13:01~17:35
▲ezKanban的儀表板畫面
只有一個Context的問題
Bounded Context在領域驅動設計(Domain-Driven Design;DDD)中是一個很重要個概念,今天先從實作面談一下有無Bounded Context的差別。以往傳統的物件導向設計,整個問題領域就是一個巨大的Context,因此在solution domain自然產生一個很大的物件類別圖, 如圖1所示。
▲圖1:Class diagram,取自JCIS
為了簡化程式設計,這個object graph之內的物件關係往往被設計成雙向連結。例如,圖2是一個用傳統OOAD所設計出的看板系統,其簡化後的類別圖:
- Team:團隊,每一位使用者至少屬於一個團隊。
- Project:專案,一個團隊有零到多個專案,可以從團隊得到屬於該團隊的專案,也可以從專案得到所屬的團隊,團隊與專案兩者之間是雙向參考關係。
- Board:板,代表一個看板。一個專案可以有零到多個看板。專案與板之間也是雙向參考關係。
- Workflow:工作流,一個板可以有零到多個工作流,兩者之間也是雙向參考關係。
- Lane:道,代表工作階段。Lane有兩個子類別,分別是代表垂直的Stage,以及代表水平的Swimlane。Lane與Workflow之間也是雙向參考關係。
▲圖2:Class diagram,取自JCIS
程式開發人員可以用team.getProject().getBoard().getWorkflow().getStage() 得到某個Stage。相反地,也可以從stage.getWorkflow().getBoard().getProject().getTeam()得到該Stage所屬的團隊。這麼做的好處是,物件之間靠著參考 (reference) 整個串在一起,操作物件感覺很方便。有優點一定也有缺點,主要的缺點有三項:
- 相依性太重:任一物件修改都可能會影響其他物件,容易產生漣波效應。
- 物件永久性與平行處裡的能力受限:為了達到整個系統交易(transaction)的一致性,物件與關連式資料庫經常透過多的表格與外鍵關係來實現。當使用者更新系統物件時,可能需要鎖定多個表格,因而降低系統的平行處理能力。
- 物件屬性與責任膨脹:整個問題領域只有一個Context,如果問題領域夠大,相同的類別在不同的功能中可能有不同的屬性與責任。例如,從Team與Project的角度,Board只是一種被分類的「東西」。除了board id與board name以外,它們並不在乎Board裡面的實質內容。但是,從Board、Workflow、Lane的角度,Board的內容是整個功能的核心。因為整個系統只有一個Context,因此只有一個Board類別,所以Board類別身上就會包含所有「客戶端」所需要的資料與方法。
***
Bounded Context
▲圖3:兩個bounded contexts
如圖3所示,有了bounded context概念,如果把原本的問題分成兩個bounded contexts:
- Project Management
- Board Management
Board同時出現在兩個bounded contexts之中,但是有著不同的意義與屬性。Project Management的Board只需要有board id、board name、project id、team id即可。而Board Management的Board才需要workflows等資料。
如此一來,當Project Management移動Board,就與Board Management的Board沒有關係,解決了上述只有一個Context所造成的三個問題:
- 相依性太重
- 物件永久性與平行處裡的能力受限
- 物件屬性與責任膨脹
區分Bounded Context還有其他好處,像是在多個Bounded Context中找出企業最具競爭力的core domain,以便區分業務優先順序以及安排開發資源。
***
代價
區分Bounded Context所付出的代價就是當企業流程或資料跨越不同Bounded Context時,需要協調這些活動與資料。例如,當Board名稱在Project Management被修改時,需要通知Board Management更新狀態。或是當Board Management新增一個Board時候,也要通知Project Management產生一個Board。
***
友藏內心獨白:要因地制宜。
請教一下:
回覆刪除>>例如,當Board名稱在Project Management被修改時,需要通知Board Management更新狀態
會有這個問題,是因為這二個bounded context 同時都被叫用時, 對嗎?
因為若在 Project Management改了Board name, 它最後會被存到db; 稍後使用者在操作 Board Management功能時, 由於會從db讀取, 自然就會讀到新的Board name了
Hi Allen,
刪除Project Management的Board和Board Management的Board,是兩個不同的物件,儲存在資料庫也是不同的表格(它們甚至可以不存在同一個資料庫,因為它們屬於不同的Aggregate)。
所以,若在 Project Management改了Board name,Board Management並不知道,除非它去聽Project Management的domain event然後Board Management再更新自己的board name。反之,也可以將change board name這個使用案例設計在Board Management,先更新Board Management的board name,之後再通知Project Management讓它去更新自己的board name。
作者已經移除這則留言。
刪除我想問的是allen所提到問題中Project Management與Board Management2個aggreagte root之間交叉的區域,藉由訂閱事件來達到一致性,您的回覆中有提到可以不存在於同一個資料庫,假設2者是可以獨立存在、資料互相分割的服務、我是不是可以理解成Project Management的資料庫其實也有一個Board的view,但欄位可能僅是Project Management所需要的,並非完整,且也不涉及Board的生命週期與狀態變化,有點類似query的概念,大部份時間是查詢的,即便是command也是因訂閱事件所觸發,核心還是存在於Board Management,所以是當其他aggreagte root需要Board的資訊維持最新的情況,藉由訂閱事件及非同步執行來整合跨context的資訊,實務上是這樣嗎?
刪除Hi stone,
刪除基本上就像你說的這樣,Board Management的Board是一個Aggregate,身上有 board id, team id, board name, board members, workflows等資料。Project Management也有一的Board,不過這個Board只是Team Aggregate的一個Entity,它的資料只有board id, team id, project id與 board name (這個board name是Board Management的board name的副本,會透過監聽 Board Renamed領域事件來更新這個副本的狀態)。
謝謝您的回覆,由於工作環境中並沒有同伴或前輩可以討論請教,只能不斷的去翻閱紅皮書與藍皮書,我想請問board在project中為什麼是entity,而不是valueobject,只需要做value equal 的判定是否相等即可變更值,而不需要entity的唯一性,entity按理會需要entityid這個pk唯一識別碼
刪除Hi stone,
刪除你說的沒錯,board在project應該是value object即可。因為ezKanban一開始Team Management的Board自己是Aggregate,後來修改設計之後被降級的Team的一個屬性。當初偷懶把它從Aggregate Root直接降成Entity,日後重構是可以再改成Value Object。
謝謝您的解釋,因不了解專案的來龍去脈,所以有這個疑問,domain的邊界確實是很難確定,隨著熟悉業務知識與需求變動都會造成domain的變更,有關ddd的文章幫助很多,讓我有不同的參照去印證,謝謝您的分享
回覆刪除Hi Teddy,
回覆刪除想針對您上面的回覆做追問~
Board Management的Board身上有 board id, team id, board name, board members, workflows等資料。
Project Management的Board身上有 board id, team id, project id與 board name。
而 Project Management的Board和Board Management的Board,是兩個不同的物件,儲存在資料庫也是不同的表格。
想問:
那這樣兩張表格不就存了重複資料:board name。
若以資料庫設計來看,是不是也不太好?因為相同的內容散落各處。(或違反正規化...我是有看到DDD書上寫有時候是違反會比較好啦)
另外,雖然已經用了訂閱的方式同步資料,但是否會因為時間差導致某瞬間資料不同步?
Hi Stony,
刪除因為Board Management與Project Management是兩個不同的bounded context, 所以它們身上的Board物件屬性不同是正常現象。資料會重複沒錯,就是靠著重複的資料拿掉這兩個bounded context的相依性,讓它們可以各自獨立開發與運行。但實際上這兩個Board還是有相依性,Board物件的「single source of truth」(單一真實來源),也就是最原始的那份資料儲存於Team Management,當Team Management裡面的Board改變狀態,會透過領域事件通知Board Management裡頭的Board更新狀態,達到狀態的「最終一致性」。
謝謝泰迪的說明,受益良多
刪除