組織領域模型與聚合概念

設計軟體的兩大原則,高內聚(cohesion)與低耦合(coupling),前者透過模組化組織程式功能以降低設計的複雜度,後者透過介面隔離以及相依性注入等技術,提高軟體開發彈性,這一部份可以透過理解並
實踐物件導向設計原則來達到目的。

為了應付模型成長帶來的複雜度,我們需要組織領域模型,透過模組化,群組相關的模型,確保系統能夠儘可能將相關的模型功能整合在一起,來達到最高程度的功能內聚要求,並經由預先設計的共用介面支援模組間的存取作業,取代直接呼叫模型物件,降低物件彼此間的耦合。

領域驅動設計採用聚合樣式,組織相互關聯的模型物件,簡化模型物件的管理。

聚合

領域物件之間,彼此會有不同程度的關聯,因此某個物件的異動必須考量其它關聯物件的影響,而開發人員面對複雜的商業軟體開發時,掌控大量物件互動衍生的複雜度,是不小的挑戰。

以資料庫為主的開發,根據資料表結構設定物件關聯,建立對應資料表的資料物件支援資料的存取,例如訂單主檔中,與客戶資料的一對一關聯,或是訂單主檔與明細的一對多關聯等等,我們必須撰寫程式碼,維護物件的關聯性以及在物件異動過程中,保持關聯物件的完整性。

例如刪除一筆訂單主檔,必須同時確保相關的明細物件亦同時刪除,這意謂著我們必須確保物件異動的過程中,關聯物件狀態的一致性,這是相當困難的過程,尤其在複雜的商業應用開發的環境下,因為物件除了彼此關聯,同時會與其它的物件有不同程度的關聯,而領域驅動設計藉由聚合(Aggregates)樣式定義群組化單元,降低關聯物件的複雜度。

每個聚合代表一個獨立的邏輯單元,定義出所要容納的物件範圍,聚合中的物件具有一定的關聯性,而聚合範圍被視為邊界,應用程式以聚合為單元執行任務。



以上的模型包含了Order、OrderLine 以及 Product與Customer等物件,支援常見的商品訂購流程,在沒有聚合設計的前提下,外部程序可以不受限制的存取其中的任何物件,這導致我們很難控制模型的存取,同時引發其它難以預測的副作用。

設計聚合有幾個重要的原則:

1. 每個聚合單元都必須有一個根物件。
2. 應用程式只能針對聚合單元的根物件進行存取,聚合單元內的物件,則需經由根物件關聯存取。
3. 某些必須獨立存取的物件,本身則形成單一物件聚合,亦是根物件。

例如要存取任何一筆明細資料物件時,不是直接取得OrderLine物件,而是透過Order,再經由Order關聯至目標OrderLine,因為在邏輯上,OrderLine是Order的關聯物件,本身並無法單獨存在,因此定義如下的聚合邊界,進一步組織原先各自獨立的物件。



圖示說明聚合設計必須注意的關鍵,由於定義了邊界,物件的存取控制權集中在根物件,如此一來,外部份程序便無法存取其中根物件以外的其它物件,避免了無法預期的物件存取,所有與聚合物件有關的存取路徑,均被限縮於根物件的單一存取,大幅降低邏輯運算的複雜度。

聚合的原理並不複雜,困難的地方在於實作上的定義,通常商業軟體牽涉大量的領域物件,其中包含實體以及實值物件,而某些關聯物件同時由其它的聚合共用,這些都需要透過通用語,與領域專家不斷的溝通,才足以真正實踐貼近領域問題的模型聚合設計。

關於實作

ASP.NET 早期必須透過ADO.NET 設計資料物件,完成底層資料庫來源的對應,並透過設計關聯來達到領域模型的實作與聚合定義,而現在無論 Web Forms 或 ASP.NET MVC,都可以進一步整合Entity Framework,透過其模型的內建功能,完成相關的實作。

對於原理有基礎的認識,歡迎參考《商業級 ASP.NET MVC 樣式與架構實務》一書,其中完整示範了 ASP.NET MVC 與 Entity Framework 整合的領域物件聚合實踐




沒有留言: