在 理解了 限界上下文 以及 分層架構 的本質基礎上 需要確認系統的代碼模型
每個團隊 無需 都遵守一套 代碼模型
在同一個項目中 必須
1遵守 同一個代碼模型 並需要
2 知道 如此劃分代碼的 意義 與價值
代碼模型設計
之前已經分析過
1 層與層之間的協作
2 跨限界上下文之間的協作
考慮限界上下文的代碼模型時,需要考慮縱向架構除前端之外的所有層次或模塊
在代碼模型設計因素中,需要考慮
1 層與模塊之間的職責分離與鬆散耦合
2 將整個限界上下文作爲基本設計單元,照顧到限界上下文之間的協作關係
示例:
application: DDD 的應用層,對應限界上下文中 所有的應用服務
interfaces: 對 gateways 中除 persistence 之外的抽象 包括以下幾方面:
訪問除數據庫之外其他外部資源的抽象接口(interface)
訪問第三方服務或其他限界上下文服務的抽象接口
備註:從分層架構的角度講,interfaces 應該屬於應用層,但在實踐時,往往會遭遇領域層需要訪問這些抽象接口的情形,單獨分離 出 interfaces,非常有必要。(領域依賴於其他服務的接口)
domain: 領域層 (純業務邏輯層 不包含技術實現) 不依賴於repositories repositories體現的是它在基礎設施層 扮演的與外部資源 打交道的語義
repositories:資源庫,皆爲抽象類型。如果該限界上下文的資源庫並不複雜,可以將 repositories 合併到 domain 中。
gateways: 基礎設施層 視外部資源的集成需求劃分不同的包
controllers放在 gateways 之下,還是想體現它的網關本質
persistence 對應了 repositories 抽象,至於
其餘網關,對應的則是 interfaces 下的抽象,如:消息隊列以及與其他限界上下文交互的客戶端
client 包下的實現類與 interfaces 下的對應接口組合起來,等同於上下文映射中“防腐層(ACL)”的概念
1 分層架構的層 最終需要映射到 模塊 或者 包 上 無法通過語言嚴格界定各層 需要架構師 與 各個RD對齊
2 無論代碼結構是否表達了層的概念,都需要充分理解分層的意義,並使得整個代碼結構在架構上要吻合分層架構的理念。
3 每個模塊或包都是單一職責的設計,在整個代碼模型中扮演着不同的角色,有的對應了分層架構的層,有的代表了領域驅動設計的設計要素,有的則是爲了保證架構的鬆散耦合。
不考慮 Repository 在領域驅動設計中的特殊性,而僅僅將其視爲一種網關,可以將 repositories 融合進入interfaces 中
controllers 對應上下文映射的開放主機服務(OHS)模式,client 對應上下文映射的防腐層(ACL)模式 得到如下代碼模型:
限界上下文的通信邊界會直接影響到代碼模型的設計決策。
進程間通信的代碼模型
order進程 調用 notification 進程
NotificationService 接口來隔離這種實現機制 NotificationClient 實現了 NotificationService接口 作爲防腐層隔離了 notification的變化
notification需要提供開房主機服務(http接口 或 rpc接口)
進程內通信的代碼結構
限界上下文之間採用進程內通信,需要注意如何在代碼模型中體現限界上下文的邊界(分包)
考慮兩個處於相同進程中的限界上下文彼此之間該如何協作,存在如下幾種方式:
-
簡單:在下游限界上下文的領域層直接實例化上游限界上下文的領域類。
-
解耦:在下游限界上下文的領域層通過上游限界上下文的接口和依賴注入進行調用。
-
遷移:在下游限界上下文中定義一個防腐層,而非直接調用。
-
清晰:要保證領域層代碼的純粹性,應該避免在當前限界上下文中依賴不屬於自己的代碼模型
遷移:在下游限界上下文中定義一個防腐層,而非直接調用。
NotificationClient 不再通過跨進程調用的方式發起對 RESTful 服務的調用,
防腐層的實現中NotificationClient 直接通過實例化的方式調用了 Notification Context 應用層的 NotificationAppService。這是在 Order Context 中,唯一與 Notification Context 產生了依賴的地方。
限界上下文采用進程內通信,也僅僅是封裝在防腐層中發起調用的實現有所不同,即前面例子中的 NotificationClient,而這其實並不影響代碼模型。
無論是進程間通信,還是進程內通信,我們設計的代碼模型其實是一致的,並不受通信邊界的影響 原因:
-
通信邊界的劃分是物理意義,代碼模型的劃分是邏輯意義,二者互相併不影響。
-
爲保證系統從單體架構向微服務架構遷移,應保證代碼結構不受架構風格變化的影響。
本書的域名爲 practiceddd,對於一個電商系統,無論限界上下文的邊界爲進程間通信還是進程內通信,上下文的命名空間都應該爲practiceddd.ecommerce.{contextname},
訂單上下文 praticeddd.ecommerce.ordercontext
商品上下文的命名空間爲 praticeddd.ecommerce.productcontext
代碼結構如下所示:
兩個或多個限界上下文還存在共同代碼,只能說明一點:那就是我們之前識別的限界上下文有問題!
在第17課“上下文映射的團隊協作模式”中,我們提到的“共享內核”模式就是用來解決此類問題的一種方法。
一旦提煉或發現了這個隱藏的限界上下文,就應該將它單列出來,與其他限界上下文享受相同的待遇,即處於代碼模型的相同層次,然後再通過 interfaces 與 gateways/client 下的相關類配合完成限界上下文之間的協作即可。