領域:
廣義上講,領域(Domain)即是一個組織所做的事件以及其中所包含的一切。每個組織都有它自己的業務范圍和做事方式。這個業務范圍以及在其中所進行的活動便是領域。
領域既可表示整個業務系統,也可以表示其中的某個核心域或支撐子域。當談及到業務系統中的某個方面時,使用”核心域“或”子域“以示區別。
為整個業務系統創建一個單一的、內聚的、全功能式的模型。這并不是我們使用DDD的目的。正好相反,DDD中,一個領域被分為若干子域,領域模型在限界上下文中完成開發。在開發一個領域模型時,我們關注的通常只是這個業務系統的某個方面。試圖創建一個全功能的領域模型是非常困難的,對領域拆分有助于我們成功。
幾乎所有軟件的領域都包含多個子域,這和軟件系統本身的復雜性沒有太大關系。有時一個業務系統的成功取決于它所提供的多種功能,而將這些功能分開對待是有好處的。
子域:
子域并不是一定要做得很大,并且包含很多功能。有時,子域可以簡單到只包含一套算法,但并不包含在核心域之中。簡單的子域可以以模塊的形式從核心域中分離出來,而不需要包含在笨重的子系統組件中。
很多軟件開發者都認為將所有東西都放在一個系統里面是一件好事。其實不管你向系統中添加多少功能,你都無法滿足每一個潛在客戶的需求。如果不通過子域對軟件模型進行劃分,事情將變得更加煩瑣,因為系統中的各個部分都是緊密聯系在一起的。
限界上下文:
一般一個限界上下文對應一個子域。另外一個限界上下文有可能包含的不只一個子域。
如一個圖書出版系統,圖書生命周期的不同階段(不同的上下文環境)
- 概念設計,計劃出書
- 聯系作者,簽訂合同
- 管理圖書的編輯過程
- 將圖書翻譯成其他語言
- 出版紙質版或電子版圖書
- 市場營銷
- 將圖書賣給銷售商或直接賣給讀者
- 將圖書發送給銷售商或讀者
以上所有階段,我們用一個單一的概念對圖書建模顯然不行,每個階段“圖書都有不同的定義”。一本書只有和作者簽訂了合同之后才能擁有書名,而書名可能在編輯過程進行修改。在編輯過程中,圖書包含了一系列的稿件,其中包括注釋和校正,走到最終定稿。圖書印刷方使用頁面布局和封面板式印制圖書。營銷員不需要編輯和印制,只需要圖書的簡介,圖書售后物流,勻人需要圖書的標識碼、物流目的地、數目、尺寸和重量等。
如果我們用單一模型來處理所有這些階段會發生什么?概念混淆、意見分歧和爭論是不可避免的。即使有時可能會得到一個正確的公共模型,但這種模型并不具有持久性。
為解決這個問題,我們應該為每個階段創建各自的限界上下文,在每個限界上下文 ,都存在某種類型的圖書。在所有的上下文中,不同類型的圖書對象將共享一個身份標識,這個標識可能是在概念設計階段創建的。然而,不同上下文中的圖書模型卻是不同的。
如果在不同的限界上下文看到了完全相同的對象,這通常意味著你的模型是錯誤的,除非這些限界上下文使用了共享內核(Shared Kernel)。
限界上下文不僅僅只包含模型。雖然領域模型是限界上下文的主要“公民”。但是限界上下文并不只局限于容納模型,它通常標定一個系統、一個應該程序或一種業務服務(表示一系統用于實現業務用例的復雜組件)。有時,限界上下文所包含的內容可能比較少,比如,一個通用子域便可以只包含領域模型。
模型應該用來驅動數據庫和用戶界面的設計,而不是反向驅動。
如下模型驅動數據庫設計。
public class Backlogltem extends Entity {
private BacklogItemld backlogltemld;
private BusinessPriority businessPriority;
}
CREATE TABLE 'tbl_backlog_item' (
'backlog_item_id_id' varchar(36) NOT NULL,
'business_priority_ratings_benefit' int NOT NULL,
'business_priority_ratings_cost' int NOT NULL,
'business_priority_ratings_penalty' int NOT NULL,
'business_priority_ratings_risk' int NOT NULL,
)ENGINE=InnoDB;
通常情況下,系統/應用程序的使用者并不只是人,還可能是另外的計算機系統。系統中有可能存在Web服務之類的組件。我們也可以使用REST資源來與模型交互,此時的REST資源即被稱為開放主機服務(Open Host Service),以上面向服務的組件都應該位于上下文邊界之內。
用戶界面和面向服務端點都會將操作委派給應用服務。應用服務包括比如安全和事務管理等。對于模型來說,應用服務扮演的是一種門面模式Facade。同時,應用服務還具有任務管理功能,它將來自用例流(Use Case Flow)的請求轉換成領域邏輯的執行流。應用服務也是位于上下文邊界之內。
限界上下文主要用來封裝通用語言和領域對象,同時它也包含了那些為領域模型提供交互手段和輔助功能 的內容。
不要用架構來指導設計,而不是通用語言。一些平臺、框架或者基礎設施通常是用來打包和部署組件,它們可能影響我們對限界上下文的設計,此時我們會從技術層面而不是語言邊界來考慮問題。
不要為了分配任務而拆分限界上下文。我們沒有必要為了管理技術資源而創建一些假的上下文邊界。
采用語言驅動來實施DDD,這里地底線:如果沒有采用語言驅動,那么你就不算在和領域專家一起工作來創建限界上下文。認真考慮限界上下文的大小,不要急于將其小型化。
與技術組件保護一致。技術組件并不能定義限界上下文。一個限界上下文通常就一個工程項目。VS中在同一個解決方案中將用戶界面、應用服務和領域對象分離在不同的子項止中是合理的。項目源代碼可以只包含領域模型,也可以包含一些周邊的層匱乏六邊形區域。
在使用java時,頂層包名通常表示限界上下文中頂層模塊的名字。