l

2021年6月22日 星期二

領域驅動設計學習筆記(16):學DCI,重構Aggregate ,Part 1

June 22 09:28~11:08


DCI簡介

最近三個月陸陸續續花了點時間研究DCI架構(Data, Context, Interaction),該架構包含三個主要元素:

  • Data:資料物件,傳統OOAD或DDD所謂的領域模型物件。DCI認為物件只需要有資料,以及存取資料的簡單方法即可。操作這些資料的演算法(methods),應該從物件身上獨立出來,放到角色(Role)身上。從這個角度來看,Data類似Martin Fowler所說的貧血模型(Anemic Domain Model)。
  • Context:實作使用案例的地方,DCI認為,物件的演算法必須放在一個特定使用情境去討論才可知道它的意義,這個情境(Context)就是使用案例。
  • Interaction:使用案例藉由指派若干物件扮演不同的角色,並協調物件之間彼此的協作來完成使用案例,稱為互動(Interaction)。角色是一個沒有狀態的物件,有點類似domain service。但在DCI架構中,Data物件可以在程式執行期間動態扮演不同角色以便獲得執行不同演算法的能力。Data物件扮演某個角色之後,該Data物件看起來就變成該角色的型別,可以執行該角色身上所定義的方法。

乍看之下DCI好像把原本富有行為的充血模型,搞成只剩下資料的貧血模型,這不是一種物件導向分析設計的壞味道嗎?其實不然,DCI的倡導者James Coplien認為:「把資料和行為封裝在類別身上是祖父級的物件導向設計方法。」DCI的Data物件在編譯期間雖然沒有行為,但其行為是在執行使用案例時動態由Context所指派。Coplien認為唯有給定一個Context再去解讀物件身上的行為才會有意義,程式也比較容易被讀懂。

***

DCI與Clean Architecture比較


▲圖1:Clean Architecture與DCI對應關係


如圖1所示,把DCI對應到Clean Architecture,Teddy認為Data與Role的實作應該是位在Entity Layer,Context與Interaction則是位於Use Case Layer。

由於Clean Architecture並沒有特別規範Entity Layer的領域物件需要如何實作,所以如果開發人員願意,當然可以用Data與Role來實作領域模型。這就好比可以用DDD的Aggregate、Entity、Value Object、Domain Service來實作Clean Architecture的Entity Layer是一樣的道理。

***

DCI與DDD比較

把DCI和DDD實作融合在一起花了Teddy比較多的時間,主要的問題在於DDD的Aggregate設計模式。Aggregate是一組物件的集合,從中指派一個物件當作Aggregate Root,所有對於該Aggregate內部物件的存取,都需要透過Aggregate Root。Aggregate同時也是交易邊界,同一個Aggregate資料存取必須發生在同一個交易之內,不同Aggregate則是透過領域事件同步狀態,達到最終一致性及可。

若將Aggregate對應到DCI,它到底屬於Data,還是Context?

看到這裡有人可能會覺得:「Aggregate不是放在Entity Layer的物件嗎,那就應該屬於Data啊。」在Teddy實作DDD的時候,的確是把Aggregate放在Entity Layer,但實際上Aggregate Root某種程度扮演了「多個小型使用案例」的角色。因為在設計階段決定Aggregate邊界的時候,很大部分的原因是依據「業務範圍」來決定Aggregate大小。也就是說,從業務需求的角度,若干物件的協作必須發生在同一個交易內商業規則才不會被違反,因此把這些物件放在同一個Aggregate。這樣聽起來,是不是和DCI的Context所扮演的角色幾乎一樣? 主要的差別在於,Aggregate是靜態的物件協作範圍,DCI的Context則是動態的物件協作範圍。

***

下集待續

先把背景知識介紹完,下一集Teddy將說明如何參考DCI的精神重構ezKanban系統的Aggregate。重構之後的程式變得更乾淨,符合單一責任原則與介面分隔原則。

***

友藏內心獨白:天下事分分合合。




1 則留言:

  1. 感覺 DCI,好像可以避免 Aggregate Root 變成便利商店店員的狀況。

    回覆刪除