iT邦幫忙

2021 iThome 鐵人賽

DAY 3
1
Software Development

淺談中台架構、DDD與Python實踐系列 第 7

【Day 07】領域驅動設計的戰術設計(Tactical Design)

前言

上一篇我們討論DDD的戰略設計,說明系統範圍如何切割成多個領域(Domain)、子領域(Sub-Domain),完成後,接著進行戰術設計,將實體進一步分類,並訂定技術架構,提供服務以操作這些實體。

DDD 戰術設計

Eric Evans 描述DDD的工作範圍如下,其中上半部為戰術設計,下半部為戰略設計:
https://ithelp.ithome.com.tw/upload/images/20211010/20001976JHZu6LMlw2.png
圖一. DDD 戰術設計

依照上圖,戰略設計(Model-driven design)要進行的工作如下:

  1. 實體(Entity):將領域內的各項實體識別出來。
  2. 值物件(Value Object):表達實體的狀態(State)及計算(Computation)。
  3. 領域事件(Domain Event):列出所有影響實體的事件,說明實體變化及狀態。
  4. 聚合(Aggregate):將相關的實體封裝為聚合。
  5. 設計模式(Design Patterns):建立工廠(Factory)、倉儲設計模式(Repository),以利於產生物件及存取。
  6. 服務(Service):將模型包裝成服務,變成API。

聚合(Aggregate)

為達到『高內聚、低耦合』(High conhesion、Low Coupling)的設計原則,會將各個實體歸類成一個個的聚合,例如客戶、訂單(保單)、產品...等,客戶聚合包括客戶主體、多個帳戶實體、多個分公司地址,其中客戶主體稱為聚合根(Aggregate Root),它具有唯一的識別欄位(ID),可以存取單一實例(instance),聚合就像是肉粽串,而聚合根就是草繩的頭,往上提就可以把聚合內所有的實體都抓起來,因此,聚合根是一個帶有業務規則的實體,是對外溝通的窗口。

因此,我們會將一個領域或子領域細分為多個聚合,每個聚合有唯一的聚合根,秉持『高內聚、低耦合』的原則,盡量減少聚合間的連結。

實體(Entity) vs. 值物件(Value Object)

值物件是表達實體的狀態(State),沒有識別的Id,例如訂單上客戶的地址,它是訂購當時客戶的地址,可能不是最新的,或是訂單上的總價或折扣,只是計算欄位,是一個快照(Snapshot)的概念,因此,一個龐大的實體盡量能拆成多個值物件,會使實體變得更容易維護,因為值物件是不可修改的(Immutable),要嘛整個刪除或是重新建立,例如下圖。
https://ithelp.ithome.com.tw/upload/images/20211010/20001976DRKoMTzJaE.jpg
圖二. 實體(Entity) vs. 值物件(Value Object),信用報告含多個值物件

Domain Object vs. Persistent Object

我們使用各種 ORM、MVC 或 MVVM 架構撰寫應用程式時,系統會要求我們建立類別,與資料庫的資料表/欄位對應,這就是所謂的Persistent Object(PO),類別內只有屬性,沒有方法,稱為POJO(Java)或POCO(C#),會讓人誤以為這就是實體,依 DDD 的設計原則來看,這是不好的『貧血模型』(Anemic Domain Model),它會使操作物件的方法全部寫在類別外的程式邏輯中,違反物件導向設計原則(OOP)、單一職責設計原則(Solid),使程式邏輯全部攪在一起,難以維護,因此,我們應該在Persistent Object上,重新組織一層 Domain Object,將相關的PO連結在一起,並且賦予相關行為的描述,即充血模型,業務邏輯應放類別裡面,例如MVVM的View Model就是Domain Object(DO),它可以是畫面(View)的資料來源,也可能是API的輸入/輸出(REST、WSDL)、網路傳輸的資料內容(Data Transfer Object, DTO)。

在程式開發時 DO 與 PO 的互轉非常頻繁,筆者使用C#開發程式時,就常使用 AutoMapper 函數庫,進行 DO 與 PO 的轉換。譬如https://ithelp.ithome.com.tw/upload/images/20211010/20001976LoEWQUtvHD.png,從請購單、採購單、到貨通知單、驗收單,多張請購單可能併成一張採購單,多張採購單可能一次到貨,也可能分批到貨,驗收時也是如此,這些單據如果都是一個物件,每個物件都會影響其他物件的狀態及內容。

Design Pattern

DDD 建議引用各種設計模式,提高生產力,例如 Repository/Factory簡化物件產生/共通方法操作,Adapter方便模組掛載,PPublish/Subscribe處理事件的傳送/接收,目前各種語言都可以找到相關的框架(Framework)或函數庫,可以直接套用,後續我們就來看看Python相關的套件。

結語

DDD 並沒有嚴格規定進行的步驟,只有一些原則(Principle),每本書籍的作者各有領悟,利用上述的原則,自行訂定詳細的執行步驟,大部份都搭配Java框架,筆者推薦『中台架構與實現:基於 DDD 和微服務』一書的說明,不過,筆者並不喜歡Java繁複的架構,所以,想使用Python試試看,它又可以輕易的整合機器學習模型。


上一篇
【Day 06】領域驅動設計的戰略設計(Strategic Design)
下一篇
【Day 08】工廠方法設計模式(Python)
系列文
淺談中台架構、DDD與Python實踐10

尚未有邦友留言

立即登入留言