記得之前在規划和設計微服務架構的時候,一個同事給我我一個至今依然記憶深刻的提示:你的設計藍圖里為什么沒有看到DDD的影子呢?
隨着對充血模型的領域認知的加深,我越來越覺得DDD的重要性,但是DDD內容繁多,是不是要深入去了解呢,我覺得不必入坑太深,個人淺見,它最核心的一點就是針對貧血模型的不足而設計,把原先傳統的貧血模型里的業務邏輯拎出來,融入到Domain層,這樣面對復雜業務的規模化變更,我們只需要專注於Domain即可。
回到主題,我們要了解的是微服務和DDD到底有什么關系呢?
因為在互聯網時代,軟件所面臨的問題遠遠比以往要復雜的多,這種復雜性來源於不斷擴展地問題域自身,也來源於創新變化,以及這種規模性增長所帶來的挑戰。
然而一個人一個團隊,它對復雜的事務的認知是有極限的,面對這種復雜問題唯一的方法就是分而治之,分主要考慮的是如何去分,治意味着分出來的每一個部分要能夠獨立的運行,能夠相互的作用,完成整體的目標,能夠應對外部變化所帶來的沖擊。
微服務的缺陷
微服務框架在分和治兩個方面都給出了很多好的理論指導和最佳實踐,那微服務是不是解決復雜問題的銀彈呢?其實不然,很多團隊在應用微服務構建他們的系統以后,發現並沒有完全解決這種復雜性問題,甚至還帶來了一些其他的問題,比如服務並沒有解決發雜系統如何應對需求變化這個問題,甚至還加劇了這個問題,當一個需求變化了,需要花大量的經理去識別這個變化影響到了哪些微服務,這些服務的多個團隊之間,需要通過無休止的扯皮去決定哪個服務多改一些,哪個服務少改一些,然后測試團隊還需要做昂貴的這種聯調測試,即使如此,開發團隊依然不放心,還要通過一系列的開關控制,小心翼翼地去做切流,去做灰度發布。
從業務層面來看,微服務框架沒有避免這種散彈式的修改,甚至反而加重了它,這是為什么呢?一個重要的原因是因為微服務框架在分的維度考慮的並不全面。
DDD功用
當我們去做分的這種工作的時候,需要考慮哪些維度呢?我覺得我們至少需要考慮三個維度:
1、功能維度
2、質量維度,比如性能、可用性
3、工程維度
微服務對第2個給出了很好的指導,對第3個也給出了一些建議,但是,對第1個功能維度,只給出來一個非常有限的指導,就是為什么隨着微服務的流行,領域驅動設計(DDD)被重新重視起來的原因。
DDD彌補了微服務在功能划分方面沒有給出很好指導的缺陷,所以他們在面對復雜問題和構建系統時候是一種互補的關系,在系統拆分的時候可以很好的協作。
只是他們看待系統拆分這個角度是不同的,微服務當中的服務所關注的范圍正是DDD所推崇的六邊形架構中的領域層。
拆分案例
接下來結合DDD和微服務來拆分一個復雜系統。
關於領域
我們稱企業的業務范圍和在這個范圍里進行的活動為領域,和軟件系統無關。領域會分成多個子域,比如我們的電商系統,會有:
1、商品子域
2、訂單子域
3、庫存子域等等
在不同的子域里,不同的概念含有不同的含義,所以我們在進行領域建模的時候。必須要有一個明確的領域邊界,也就是DDD里稱作的限界上下文,它是系統內部的一個架構邊界,決定了這個系統架構。
划分系統內部架構邊界
架構簡潔之道這本書里說過:系統架構是由系統的內部架構邊界以及邊界之間的依賴關系所決定的,與系統中各個組件之間的通信和調用的方式是無關的。我們常說的微服務的服務調用本身只是一種比函數調用方式成本稍高的,分割應用程序行為的一種形式,系統架構無關。
所以,復雜系統划分的第一重要的是要划分內部的架構邊界,即划分清除這個上下文,以及明確他們之間的關系,這對應於我們之前所說的功能的維度。正是DDD用武之處。其次我們才考慮基於非功能的維度如何划分,這是微服務能夠發揮其優勢的地方。
舉個例子,我們把系統分成ABC三個上下文,三個上下文的代碼可以在一個部署單元里運行,通過進程內調用完成操作,這就是典型的單體架構。也可以各子在一個獨立的部署單元里運行,通過遠程調用來完成操作,這就是現在流行的微服務架構。
邊界清晰的好處
我們更多的是兩種架構模式的一個混合,比如A和B一起是一個部署單元,C是另外一個獨立的部署單元,這種情況往往是因為C非常重要,他並發的訪問量非常大,或者它的需求變更比較頻繁,將C拆分出來的有以下幾個好處:
1、資源傾斜
2、使用彈力設計模式,比如重試,熔斷,降級
3、使用特殊技術,比如GO語言
4、具備獨立代碼庫,有獨立開發團隊和運維人員,和A和B運行期做到隔離不互相影響
這四點正式微服務架構所關注的,它是基於肺功能維度的視角來看待拆分這件事的,它關注的不是系統架構的邏輯邊界,更多的關注是應用程序行為的分隔。
那么為什么不把A和B都拆成一個獨立的部署單元呢?
這會帶來更多的好處,也會帶來額外的成本,架構應該是可以演進的,在業務發展的早期,應該關注系統架構邏輯邊界,保持邏輯邊界的清晰和關系的正確,隨着業務量的增加,逐步在做拆分,這是組合應用DDD和微服務架構帶來的最大的好處。
在單體架構中,保持架構邏輯邊界不被突破是有一定難度的,如果邏輯邊界不清晰,在需要服務器拆分的時候,就未必能拆分的出來了,另外沒有人一下子就可以把邏輯邊界定義正確,即使這個上下文定義的不正確,在DDD聚合根這個概念可以保障我們能夠演進出更適合的上下文。
DDD界限上下文內部通過實體和值對象來對領域概念進行建模,一組實體和值對象歸屬於一個聚合根,那按DDD要求
1、聚合根用來保證內部實現規則的正確性和數據的一致性
2、外部對象只能通過ID來引用聚合根,不能引用聚合根內部的實體
3、聚合根之間不能共享一個數據庫事務,他們之間的數據一致性需要通過最終的一致性來保障。
有了聚合根,基於這些約束,未來可以根據需要把聚合根升級為上下文,甚至拆分成微服務都是比較容易的。