閱讀目錄 前言 六邊形架構 終於開始建項目了 DDD中的3個臭皮匠 CQRS(Command Query Responsibility Segregation) 結語 閱讀目錄 前言 六邊形架構 終於開始建項目了 DDD中的3個臭皮匠 CQRS(Command Query Responsibilit ...
閱讀目錄
一、前言
上一篇我們講了DDD的核心概念(附上鏈接),並且設計了我們的上下文映射圖,那麼接下來就準備開始立項了,本篇文章的部分知識點可能對一部分人來說比較基礎,可以選擇性的閱讀。
在這之前我們平常用的最多的應該就是3層架構了,這裡也不展開描述了,大家都是在3層的陪伴下一路走來的~
DDD所使用的傳統分層架構是鬆散分層,也就是上層可以訪問任意層級的下層,而不是僅限於當前層的下一層,這是有別於3層架構的。如下麵2張圖的區別圖:
【圖1】
【圖2】
Application:這層的職責是對接收到的數據做一些非業務性驗證,事務的控制,最重要的是協調多個聚合之間的操作。這裡應該可以清晰的表達出整個操作所做的事情,並且與通用語言是一致的。
Domain:這一層是DDD設計的核心,這裡不但需要精確合理的表達出通用語言的每一個細節,另外如何把對象合理的定義為聚合、實體、值對象也是重中之重。這裡不但關係著整個項目的複雜度,也是戰術建模的體現,任何的一行代碼都是對業務的準確定義,應該是恰到好處。一個清晰簡潔的戰術建模才可以應對後續的快速變化。
Infrastructure:這裡是輔助性的一層,也是整個項目的基礎。好比這裡存放著一磚一瓦,最終建造什麼模樣的高樓在於用它的地方。主要包括,倉儲的實現(我們存放數據的地方)、一些通用的支撐性類庫。
二、六邊形架構
在[Vaughn Vernon]《實現領域驅動設計》一書中多次提到對DDD主張六邊形架構的概念,六邊形架構對於保證限界上下文內的領域概念的清晰性有著重要的作用,那麼什麼是六邊形架構,如下圖3(摘自[Vaughn Vernon]《實現領域驅動設計》一書)。
【圖3】
在當今越來越提倡開放合作的大環境下,引用的多樣化的Service,和在自身系統達到一定規模之後的分散式治理,越來越需要通過協作進行工作,那麼如何提升協作的效率變得越來越重要。提高各個應用程式的自治性,是一種有效提升協作能力的手段。從上圖中看出為了保證領域模型所在的應用程式的乾凈簡潔和自治性,各種適配器作為"防腐層(在上篇中有提到)"在整個程式的最外層保護著當前的“界限上下文(在上篇中有提到)”不受外部入侵。
所以在我們的整個設計中需要註意對涉及到外部系統交互的地方的抽象,通過面向介面、依賴註入等方式做到外部的變化對自身系統的影響最小化。
三、終於開始建項目了
按照之前的這些描述,我們終於初步建立了我們的解決方案。如下圖4:
【圖4】
這裡把每個項目的職責大致說一下。
Mall:負責我們的電商網站的界面處理和用戶的數據錄入
Mall.Application:按模塊分別定義不同的ApplicationService來講述每一個操作下的“故事”。
Mall.Application.DomainEventSubscribers:所有的領域事件訂閱者。
Mall.Domain:這裡存放著戰術建模的結晶,Entity、Aggregate、ValueObject。(下麵會具體講述下)
Mall.Domain.Events:所有的領域事件,這層也可以合併到Domain中,給它新建一個文件夾。
Mall.Domain.IRepositories:所有的倉儲(資源庫)介面。類似於三層中的IDAL。
Mall.DomainService:領域服務,存放著那些不適合放在聚合/值對象上的無狀態的操作方法,用於實現特定某個領域的任務。
Mall.Infrastructure:存放著一些通用類庫
Mall.Infrastructure.Repositories:所有倉儲(資源庫)的實現。
Mall.Infrastructure.Translators:翻譯層,也就是與外部系統溝通的橋梁,主要的職責就是做好“反腐層”的重任。
四、DDD中的3個臭皮匠
這裡的3個臭皮匠其實就是:Entity、ValueObject、Aggregate。我們要提煉出業務中的精華,合理的抽象為這3個概念,並且這種抽象是需隨著領域里的概念變化而變化的。這3者的結合運用會讓我們的項目活起來,這是DDD的核心。這裡再把這3個概念重新梳理一下。
Entity(實體): 每個實體是唯一的,並且可以相當長的一段時間內持續地變化。我們可以對實體做多次修改,故一個實體對象可能和它先前的狀態大不相同。但是,由於它們擁有相同的身份標識,他們依然是同一個實體。
ValueObject(值對象):值對象用於度量和描述事物,當你只關心某個對象的屬性時,該對象便可作為一個值對象。實體與值對象的區別在於唯一的身份標識和可變性。
Aggregate(聚合):聚合類是實體的升級,是由一組與生俱來就密切相關實體和值對象組合而成的,這整個組合的最上層實體就是聚合。
五、CQRS(Command Query Responsibility Segregation)
說到DDD必然要提一下CQRS,我認為CQRS和DDD的關係就像咖啡和牛奶,給大型系統的構建提供了一劑良藥,它生於讀寫分離,具有高吞吐量、高伸縮性等特點,值得我們為之付出一些代價。但是CQRS的使用會使整個數據持久化和查詢的鏈路拉長,並且工作量也會比簡單的讀寫一體化大的多,所以需要對項目做出合理的考量來決定是否使用。
當我們需要把某個複雜的聚合修改之後寫入到資料庫的時候,要保證N張表的數據被同時修改成功,整個事務的周期必然會加長。而且當我們需要顯示來自不同聚合類型與實例的數據時,我們的SQL必然包含N多的join。領域越複雜這種情況越發常見。
CQRS需要和事件源結合使用,對數據的修改操作只是往事件源里增加一條修改後的結果記錄(類似於我們的源碼控制軟體的log),並不會直接把修改後的對象持久化到資料庫。這樣能夠大大提高數據修改的速度,並且對於查詢操作的實現方式就比較多樣化了。大致列舉了以下4種方式:
1.還是使用單個資料庫,每次領域對象的獲取都需要根據事件源中的事件集合做重建,得到當前的最新的數據返回。這隻是編碼設計上的讀寫分離
2.拆分為讀庫和寫庫,實現方式同1
3.拆分為讀庫和寫庫,並且針對讀庫做專門的查詢數據冗餘,非同步的通過事件源來修改查詢數據,可以結合merge commit。
4.在3的基礎上做讀庫的負載均衡
這4種方式複雜度各不相同,可以結合實際項目的複雜度擇優選擇,其中最關鍵的一條便是是否存在大量的列表類數據展示,如果是那麼1和2便就不適合了。在以上的方式之外可以結合其他的數據存儲一起使用,如緩存,NoSql,然而這隻需要訂閱所有的命令事件即可實現。
六、結語
本篇主要介紹了項目的分層架構、每層的職責和裡面存放什麼樣子的類。限於非我們這個系列的核心主題,所以都沒有發散出去做更加具體形象的描述,希望大家可以邊結合[Vaughn Vernon]《實現領域驅動設計》一書的閱讀跟著我做實際的編碼來加深對DDD的理解。跳出根深蒂固的三層思想是痛苦的,但是我認為只要堅持下去,DDD會讓你看見一片世外桃源,到那時會覺得我們的付出都是值得的。並且DDD思想的運用可大可小,小到類的設計,大到複雜項目之間的構架,都會給你提供幫助。
作者:Zachary_Fan
出處:http://www.cnblogs.com/Zachary-Fan/p/6012454.html