
從我們作為業務開發主要的職責深入到DDD的本質是什么?復雜度應處理?規范設計怎么做?本文將全方位為大家解答,
在文章的開始我想和大家一起思考一個問題:作為一個工程開發,我們最主要的職責是什么?
我極度認可 <<淺談什么是技術一號位>>文章的觀點 - 切實解決業務問題才是每一個工程開發最主要的職責 - 所以每個業務開發都必須要結合業務的視角去思考自己系統的建設和發展,而不是只是做一個“編程的”碼農,
-
技術一號位是負責使用技術能力解決業務問題,提供穩定可靠的技術支撐;
-
負責向業務各方提供各種必要的技術支撐,通過合理的資料分析為業務決策提供依據;
-
通過對技術領域的積累和發展,通過業務領域的理解和落地影響業務決策;
-
負責構建梯隊完整、能力全面、制度完善的技術團隊來支撐業務發展,
文中也提到了雖然不是每個人都負責一塊完成的業務,也不是每個人都帶領團隊,但是至少每個人都是自己所負責的那塊系統的技術一號位,
那實際業務開展中,業務到底會遇到有哪些問題呢?我們按業務的生命周期進行切分,然后具體查看每個業務生命周期的訴求:
-
業務啟動期:業務能力快速搭建 - 系統提供快速試錯的能力
-
業務發展期:業務能力擴展 - 系統需要支持原來越多的業務功能
-
業務平臺期:業務能力復制 - 系統需要支持原來越多的業務場景
-
業務衰退期:業務能力創新 - 系統提高生產力延長業務的生命周期

我們技術要做的事情是:在業務驗證沒有問題的情況下,如果盡可能的延長業務的發展和平臺期,讓業務獲取的利益最大化,所以為了支持業務的發展,業務的本身的功能支持訴求以及業務對技術的要求也會越來多,在這種情況下考驗軟體開發人員的一個非常關鍵的能力就是: 軟體復雜度的控制的能力,
軟體復雜度其實是一種多維度的概念,其可能來源于多個方面,前阿里資深技術專家李運華在他的《從0開始學架構的》課程中從6個方面闡述了軟體復雜度【2】,列舉如下:
DDD本質上我認為就是一種減低軟體復雜度的手段, 其推薦的方法論可以適用于上面包括了業務規模,可擴展性兩個維度的復雜度應對,其實業務規模的復雜度的處理包括了對可擴展性的支持,
DDD實施給系統之后,我們依然需要關注系統其它的復雜度,這里列舉一些示例措施:
那么我們進一步對業務規模的復雜度進行拆解,又分為下面兩類:
2、技術實作的復雜性
代碼沒有按照業務系結的”分析模型”去編碼,軟體變成一個大泥潭
那DDD是如何處理上面提到的軟體復雜度的?
-
-
提供一個一系列的戰略模式:限界背景關系的映射,分層架構等,
-
DDD不是什么?
DDD的原名是模型驅動的設計方法:通過領域模型(Domain Model)捕捉領域知識,使用領域模型構造更易維護的軟體,
DDD的核心思想,大家都清楚,就是分析模型要和代碼模型保持一致,
如果技術實作和業務實作不在用一水平線上,那技術模型的行進路線只會考慮劈開技術障礙并且可能會撞在未來的業務障礙的墻上,這樣就很容易出現,業務持續演進等技術想實作的時候,卻發現當前的實作依賴于“業務不會這樣發展”的假設上,這也是為什么會出現現在眾多業務需求,技術無法實作或者是需要花大量時間去實作的原因,
但是如果技術和業務通過統一語言打破知識的壁壘保持一致,那么如果后面技術遇到問題即是業務碰到的問題,業務人員需求的變更和迭代會自然而然的幫助技術同學越過一些門檻,也就是說業務方與技術方參與到對方的作業中,就在雙方之間帶來了更好的協同,形成1+1>2的功效,
根據百度百科的解釋【3】 在軟體工程中,問題域是指待開發系統的應用領域,即在客觀世界中由該系統處理的業務范圍
這里直接摘抄一段前阿里P10"阿白"在阿里內部發表的域模型的觀點:
域模型(domain model)英文又稱為問題域模型(problem space model),維基百科(Wikipedia)對它的定義是” A conceptual model of all the topics related to a specific problem” 可以翻譯成:“域模型是針對某個特定問題的所有相關方面的抽象模型”,這個定義有幾個要點:第一是“特定問題”, 也即是說域模型是針對性某個問題域而言的, 脫離的這個特定問題,域模型的構建其實不存在一個最優或者是最合理的構建,第二是抽象, 域模型是一個抽象模型, 不是對某個問題的各個相關方面的一個映射, 也不是解決方案的構建,
在 DDD 中,Eric Evans 提倡出一種叫做知識消化(Knowledge Crunching)的方法幫助我們去提煉領域模型,簡單來說就是五個步驟:

開發人員和業務專家在一起通過一個個業務用例仔細討論應用程式的應用場景,從而使得業務人員深刻理解業務知識,開發人員和業務人員就重要的業務概念建立起統一的語言,開發人員將這些概念根據業務用例的背景關系抽象出模型,并且這些模型將會最終成為最終軟體實作中的領域模型,隨后隨著更多的業務用例的輸入,開發人員和業務人員會逐漸對已經構建的模型進行精化,并且也會用新的用例去檢驗之前構建模型的合法性和適用性,
DDD在這一步其實沒有給出詳實標準的如何建模的方法,畢竟建模還是來自于每個人的世界觀,其程序還是傾向于經驗的,但是還是有不少人總結一些標準的建模方法論例如:
1 四色原型法
http://apframework.com/2020/03/22/ddd-color/
2 用例分析法
https://baike.baidu.com/item/%E7%94%A8%E4%BE%8B%E5%88%86%E6%9E%90/2859078?fr=aladdin
大家應該發現上面的知識消化的流程是一個非常耗時和復雜耗腦力的程序, 涉及到產品,業務,技術等多方團隊, 所以為了讓有限的資源投入到最最核心的子域,我們需要對問題域進行這份,把重點的精力放到最核心的領域上,
核心領域一定是業務價值最高的,而非技術難度最高或者是基礎設施框架部分,
1 通用域: 非應用獨有的,多個應用都會有的功能,例如發送郵件,觸達等
2 核心域:和競爭對手區別開來的區域,或者是在市場上被賦予了競爭優勢的區域,
-
-
手動處理程序阻止了他們進行了根據創造性, 有附加值的作業
-
-
取哪些提示,取決于業務系統的性質,
那如何決定支撐子域/通用子域,以及支撐子域/通用子域的切分呢?
目前在我查閱的資料中,還暫時沒有人提及到具體的操作方法,感覺主要還是依靠經驗主義在做劃分,我個人總結了一個方法,主要就是就是關注業務的核心物體和核心流程,以核心物體和核心流程作為切分支撐子域的基礎,
核心物體:核心物體是存在于核心流程中,對核心流程的決策和扭轉可能起到關鍵的作用,有的時候業務上為了能讓核心物體在業務流程中起到更大或者更高效的作用,會添加一些讓核心物體更好服務于業務流程一些業務功能,從而使業務物體從整體上看變得相對復雜,這個時候我們應該以核心物體為基礎進行切割,把所有和核心物體CRUD相關的操作還有讓其變得更高效的業務功能劃分為單獨的一個領域,
核心流程: 當某個業務流程足夠復雜也可以當成一個子域,
在實作領域驅動設計【4】書中,提到了為在線拍賣網站系統劃分問題域的一個例子,我們以此來驗證上面等構想
-
賣家 + 會員身份:這兩者都是核心物體,網站可能為了讓促進會員能夠多參與拍賣可能提供了分層,或者積分等工功能,網站為了能讓賣家能夠更加提供更加有拍賣價值或者是轉化率高的品類可能為賣家提供了資料分析等業務功能,
-
名冊:這也就是核心物體,網站會對名冊提供一系列拍賣相關的功能,例如倒計時,一口價等,所以也需要形成一個領域,
-
-
爭議解決:買賣家的售后沖突解決流程向來很復雜,所以會獨立成為一個域無疑,
四、復雜度處理-進一步降低問題域的復雜度-限界背景關系
一般情況下,一個復雜系統由一系列的模型來表示解答域, 理想狀態是一個子域一個模型,但是有些當業務需要且系統復雜的時候,一個模型可能被多個域共享,這個時候這個模型的概念可能變得不清楚,因此為了保護這些模型概念的完整性, 清晰的定義模型的責任邊界很重要,
為了維護模型的概念的完整性,最直觀的方法就是為這個模型化一個邊界,e.g. 這個商品所表現的意思就是履約的時候用到的"商品",而不是下單的時候的"商品",只要有一個這樣的邊界定義,系統就會但是出現多個邊界,畢竟"商品"在不同業務背景關系中有不同的含義, 例如庫存域的貨品,物流域的運輸品, 價格域的商品等等,這樣的一個邊界就是DDD的“限界背景關系”,
限界背景關系給人直觀的感受其實和子域很像,我很早以前曾讀過一些關于微服務的書籍,也提到過要把DDD中的限界背景關系作為微服務劃分的重要依據,這里其實就給我很大的疑惑:
1 限界背景關系到底是怎么劃分的?我們劃分限界背景關系難道真的是用一個基礎概念,然后找這個基礎概念不同的“背景關系”嗎?
所以在DDD中是把限界背景關系作為某個子域的內部模塊的劃分,其實無論是子域的劃分,限界背景關系的識別,和聚合的劃分他們的本質是一樣的,他們都是對復雜問題的分解之后,然后歸類分組,只不過“聚合”面向的是領域層內部,“領域”劃分面向的是業務問題域,而“限界背景關系”面向的是解答域,但是我跟傾向于把限界背景關系理解為更加深一層次的業務問題域的劃分,而不是面向的解答域,
如果這樣看的話,那么其實就可以回答上面的疑問, 領域和限界背景關系沒有本質的區別,就像樹的父節點和位元組點一樣都是樹節點,而限界背景關系的劃分完全可以使用子域劃分的理論,(可以回顧下上面問題域拆分的段落)
背景關系的映射是什么, 簡單來說就是描述不同背景關系之間的關系的描述,舉個例子
DDD對于限界背景關系直接提煉了幾種方式,這里這邊阿里內部文章《領域驅動設計:軟體復雜性應對之道》解釋的比較好,描述如下:
共享內核 shared kernel :通常是共享核心領域或者是一組通用子領域,
customer/supplier
客戶/供應商關系 customer/supplier:上下游關系,不同客戶需要協商來平衡,上游團隊需要有自動測驗套件,
conformist
跟隨者模式 conformist:單方面跟隨模式,上游的設計質量較好,容易兼容,可以采用嚴格遵循上游團隊的模型,
anticorruption layer
防腐層 anticorruption layer:防腐層、隔離層,使用 facade or adapter 等模式,可以減少其它系統變動對本系統的影響,
separate way
各行其道 separate way:宣告一個與其它背景關系毫無關聯的 bounded context,使開發人員能夠在這個小范圍內找到簡單、專用的解決方案,
open host service
開放主機服務 open host service:開放子系統供其他系統訪問,其核心思想是開放出一個標準的各個領域都認可的協議,減輕各個領域實施ACL的負擔和成本,
published language
共享語言 published language:把一個良好檔案化、能夠表達領域資訊的共享語言作為公共的通信媒介,必要時在其它資訊與該語言之間進行轉換,
在當前電商領域的范疇,目前我個人覺得只有ACL,Seperate Way, publish language 有比較好可行性,其他的關系都不是很靠譜:
-
shared kernel:如果使用共享二方庫,誰來維護這個二方庫,如何防止在不同背景關系使用不同kernal版本所帶來的問題,
如果一定能保證shared kernel的維護在一個團隊內,且所有使用shared kernel版本一定能保持一致, 那是可以使用的,
-
customer/supplier:我曾經因為匯率包升級而去重構一個應用,因為匯率包變更太大,且應用沒有防腐層,所以不論從開發還是測驗都是非常痛苦的程序,
-
conformist:和customer/supplier類似, 但是在互聯網領域沒有靠譜的設計, 只有有人維護和沒有人維護的設計,conformist從長期來看其實就是customer/supplier,
-
Open Host Service 沒有任何一個領域保證自己的介面一定不會變,就算不會,其他領域的同學會相信嗎,他們會忍住不用ACL嗎?如果他們用ACL,OHS的意義何在?
-
publish language 目前阿里內部MTOP,TOP等協議正是使用這樣的協議,
另外限界背景關系之間真的能夠隨便無規則無條件的互相依賴,互相呼叫嗎?在下面的章節將會解釋論述,
架構分層主要的作用就是關注點隔離,如果和今天的話題聯系起來就是領域模型和技術的關注點隔離(領域和存盤,領域和展示),
2、沒有領域的概念,所有的邏輯沉淀到service中,
所以傳統架構只能針對小型的,沒有過多的業務邏輯場景,由于這種架構能夠保有領域能力的沉淀,所以在現在電商業務場景基本不會被使用,
Alistair Cockburn在 2005 年時演示了 六邊形架構
從六邊形架構開始,其強調了領域模型,并且確立了領域模型的核心位置,以及其不應該依賴于其他的層次,六邊形架構也強化了配接器的概念,其還把配接器分類為input配接器,和output配接器,所有input配接器用于對接不用的外部請求形式, 所有output配接器用于對外部的依賴 (e.g. 資料庫, 外部服務,記憶體呼叫等)
這種結構模式樹立了以領域模型為核心的先河,但是其忽略了在領域層中跨模型業務邏輯的實作方式-領域服務的沉淀,這也間接導致了其需要強化應用層,并且通過應用層和output配接器的聯合去完成一些可以應該在領域層應該完成的事情,
洋蔥架構的提出更加進化了一步,推出了域服務層,并且支持域服務層是支持了那些需要多個領域物體聯合中作用的領域邏輯. 其層次由外向內依次是領域模型,領域服務,應用服務和外層的基礎設施和用戶終端,其依賴的關系也只能是由外向內. 在洋蔥結構中其把存盤層,檔案系統和網路服務放到了基礎設施層,由于基礎設施和用戶終端一樣在最外層,所以洋蔥架構也提倡用依賴倒置來解決應用邏輯和基礎設施的耦合問題,
洋蔥架構的架構圖從其依賴順序上來看,其依賴應用層必須先依賴域服務層,再依賴域模型層, 這樣很容易造成領域模型的邏輯外泄到領域服務層,造成領域模型變成貧血模型,
DDD的架構大家都非常熟悉了,領域服務和領域模型都歸屬域領域層,適配層依賴域應用層,應用層依賴域領域層,也可以直接直接呼叫基礎設施層(大多數是查詢場景),領域層理論上不依賴于任何層次,其通過依賴倒置和基礎設施層產生關聯,在DDD架構中,應用層是可以通過直接訪問聚合根(某個物體類),并進行方法的執行和操作的,應用層也可以直接訪問基礎設施層,可以看出DDD的架構其實更加的貼切實際一些,
上面這張圖也很好的闡述了DDD各個架構層次依賴的關系,
很多情況產品構建出來的資料展示,需要橫跨幾個領域的資料的支撐,也就是我們日常構建的大寬表,在這種情況使用CQRS模式可以完美解決這個問題,其主導視圖模型和領域模型分開,讓領域模型更加專注業務邏輯,流程和規則而非業務視圖,
CQRS的思想很簡單,就是把服務中對資料的更新操作(Command)和讀取操作(Query)分離, 一部分邏輯只處理和資料更新有關的業務,另外一部分只處理和資料讀取有關的邏輯,這種處理方式,可以讓我們辛苦構建的領域模型不被業務中所需要的這類視圖需求所干擾,
在我們自己的應用中我們構建了基于COLA【5】規范的層次架構,如下圖:
1、依賴關系(除了依賴倒置)只能是從上當下;
2、同層之間永遠不能互相依賴;
3、如果同層之間需要互相用到對方的服務,那么就需要下沉出一層,例如在上圖中,我們的業務層就分為了兩層 "Executor"層次和 “Handler”層此, Handler層次用來保存業務的一些通用邏輯,
從這一章節開始介紹DDD的"戰術模式",也就是向大家介紹DDD是如何構建和組織自己領域層的,值得一提的是在DDD中,領域的劃分, 領域層次的建立, 領域之間關系的建立我們一般叫做DDD的"戰略模式",而此章節提到的值物件,物體,域服務,工廠,repository, 聚合/聚合根, 領域事件等都是DDD的戰術模式,戰略模式的重要性是要遠大于DDD的戰術模式的,我們如果在領域劃分,領域通信協議,分層方面沒有大的問題, 那么即使再糟糕系統整體也還是可控的,
在領域層面, DDD通過聚合/聚合根的概念來劃分單個領域中的類似于類集合的邊界,從而降低單個領域層的復雜度,DDD通過物體,值物件,領域服務,repository, factory 來規劃集合內部的類組織, 另外DDD也通過領域事件來處理領域之間的互動,來匹配異步和需要解耦的業務場景,
當我們需要考慮一個物件的個性特征,獲取需要區分物件的時候,就需要引入物體,一般我們發現物體概念,是在和業務產品人員或者領域專家討論發現的那些需要有唯一標示性或者生命周期連續性很重要的時候,
舉個例子加入用戶需要預定酒店,如果領域專家說了我們定了A酒店了,就不能定B酒店了,哪怕A,B其他的屬性完全一樣,從領域專家扣中我們可以識別出酒店是有唯一標示性的,且哪怕A,B屬性一樣,也不能認為A,B 是一樣的,這也說明了酒店的唯一性不是從屬性來的,這兩點我們可以推斷酒店是一個物體,唯一標示性可以是現實有意義的,例如工商注冊號,也可以無現實意義,例如資料庫主鍵,
物體建模的注意點
2、驗證和不變行
物體必須自己負責自己保持自己狀態的合法性 (validation) 和不變性(Invariants),他們的區別是合法性是根據背景關系的,而不變性是不用考慮背景關系且必須正確的,例如酒店必須有房間這個就是不變性,而酒店的營業時間就是validation. 一般使用規則和規約模式來實作validation和invariants,
規模模式:
https://baijiahao.baidu.com/s?id=1717403406288752234&wfr=spider&for=pc
不要暴露屬性給外面,如果外面得到屬性,很可能就自己實作了一些領域邏輯,那么領域邏輯就外漏了,
需要警惕物體邏輯膨脹,從而混繞了物體所要表達的概念,
例如預定是一個物體, 現在要加上邏輯預定的天數不能小于N天,這個時候我們可以為Booking 抽象出 Stay 物件,讓Stay物件去管理規則邏輯,而不是讓預定這個物體去做,讓預定只關注預定,
不要過度設計,只要滿足需求就好,不要讓技術需求污染領域設計,除非真的萬不得已,
不需要讓領域概念橫跨多個bounded context, 如果我們域模型所涉及的概念橫跨了,我們就需要用兩種設計方法.
-
-
什么時候需要使用到值物件?
e.g. 拍賣系統的能夠一口價獲取拍賣的價格, 就算是我們用一個int 就能表示也需要用類來凸顯概念
public int Price { get; private set; }
值物件的特征
-
-
-
富含行為:值物件實作業務概念的抽象,其也有自己的行為
-
內聚:將不同的相關屬性組成一個概念整體,例如Money, 是由一個long 和一個currency組成的
-
不變性:值物件是不變的物件,如果需要改變屬性,那最好是建立一個新的物件并且進行值物件替換,如果一定是需要改變,那就需要考慮設定為值物件是否合理,
不變性是值物件非常重要的一個屬性,是可以保障值物件不會被"壞味道"代碼侵入的一個原則之一, 例如如果一個值物件引入了另外一個類實體, 另外一個值物件也引入了相同的類實體, 如果值物件允許改動,當一個值物件對這個類實體的內容進行修改,勢必會影響另外一個值物件, 所以最安全的方式還是通過物件替換的方式,
什么時候用域服務
發現和多個物體相關聯,但是放入任何一個單獨的物體都不適合,這個適合用域服務.
域服務應該包含什么內容
域服務應該包含業務/系統流程和業務規則,不應該包含技術的元素在內,技術的元素都應該在業務服務(Application Service)中實作,
應用服務與領域服務的區別:
-
應用服務里不要處理業務邏輯,只在領域服務里處理業務邏輯,(如何判斷某段邏輯是否是業務邏輯?)
-
領域服務掌握領域知識,而應用服務只是對領域服務的編排,
-
應用服務是領域服務的客戶方,也就是說應用服務會呼叫領域服務里的方法,
-
當領域中的某個操作程序不屬于物體或者值物件的職責時,需要將個操作放在領域服務中,而且確保領域服務是無狀態的(這句話很有意思,也就是說領域服務中不應該有任何記錄狀態的行為,在任何情況下呼叫這個服務,它都不會有副作用,也就是說它是個純記憶體操作),
-
領域服務中包含的是業務邏輯,而應用服務關注的應該是安全和事務等非業務邏輯,
-
對事務的管理絕對不能放在領域服務層,事務管理需要放在應用服務層,因為和領域模型相關的操作的粒度都很細,無法用于事務管理,而且領域模型也不應該意識到事務的存在,
-
通常的可以放在應用服務中的邏輯有:引數驗證、錯誤處理、監控日志、事務處理、認證與授權,
除了第一條之外,上面的條例只是舉出了應用服務與領域服務兩者非常易于告之的差別,但是會有一些“業務邏輯”比較難以取舍, 例如
A ->B
A.accountDecrease(10);
我們在現在可以非常肯定的說上面的轉賬一定在領域服務,因為"轉賬"就是一個領域概念,但是如果假設世面上所有的銀行以前只有存錢和取錢兩種功能,"轉賬"是一個新概念和業務的時候,技術就沒有那么容易判別轉賬是一個臨時性的一次性的需求,還是會長久發展,這個時候技術有兩個選擇:
2、讓應用服務獲取A,B的物體,然后在應用層直接呼叫方法,在應用層做事務保持一致性,
如果域能力在其他團隊手里,我相信大多數的團隊會使用第二種,那遇到這種情況我們到底應該怎么辦?我個人的意見和阿里前技術高級專家張建飛在他的文章《一文教會你如何寫復雜業務代碼》的觀點保持一致:

我們在新邏輯出現難以判斷的時候優先講能力放入到app層,如果我們發現會有第二個業務場景使用到了相同的能力,就需要考慮是否應該把此能力下發到領域層以增強內聚性和復用性,
-
-
-
工廠方式不是在任何構建物件的時候使用,一定用在物件構建邏輯復雜,有子依賴或者是有invariant規則的場景,
reponsitory主要用來處理集合根的存盤和獲取的,提供一個facade介面且是面向domain層的,是domain model和data model的橋梁,其最大的作用就是通過反向依賴的方式充分隔離資料層和領域層,Repository最常見的用法是被applicaiton service層去使用獲取聚合根,
在repository實作中,我們一般會有下面的一些邏輯:
repository的反模式
List<Customer> findBy(CusomterQuery query)
-
使用了延遲加載,延遲加載就是設計錯誤的標志, 有可能說明我們聚合的邊界不催,
-
不要為了報表的訴求使用reponsitory, 領域的case和業務報告很不一樣,可能需要多個聚合的資料,這種情況可以考慮用一個離線的store去做,和其他的讀服務去做,不見得一定需要用領域的Repository模式,
領域事件所想要解決的問題其實和metaQ訊息機制想要解決的問題一致,都是跨領域驅動型業務邏輯實作的最佳方法,讓領域和領域之前解耦,
領域事件消費教科書的說法是可以在領域層, 也可以在應用服務層, 但是我覺得領域訊息如果用metaQ是這樣的訊息中間件去實作的話,那用領域層和應用服務層去消費就不是很方便,有可能破壞一些分層原則,所以我個人傾向于在adapter層去承接消費,統一化掉,
聚合是什么?
其實聚合的原理和領域劃分,限界背景關系劃分的原理是一致的,都是為了通過歸類分組的方式讓整個系統宏觀上 N * N 的關系復雜度減低為 T * T 的復雜度,
如何劃分聚合
1、根據業務規則和不變數來決定,例如 customer 聚合是有一套業務規則來維持的,例如信用卡要存在,必須先有一個customer, 有customer 必須有address, address必須有code.
2、強關聯的物件應該放在一塊,什么是強關聯,那就是必須生命周期是一樣的,例如customer和creditcard在,電商網站中,如果customer被洗掉了,那么他的信用卡也應該被設定為失效的,而訂單和客戶不一樣,客戶下了個訂單,然后客戶注銷,但是訂單還是一直存在的,所以customer 和 creditcard 在一個聚合中, 而用戶和訂單則不在,訂單里面可以有客戶的ID或者是一個值物件,
3、靈活設定:有些可以根據業務情況,進行可以靈活的設定,下面列舉一個論壇系統,帖子和回復聚合思考的例子:
大家都知道一個帖子有多個回復,沒有帖子,回復就沒有意義;所以很多人就會認為帖子應該聚合回復;但實際上不需要這樣,如果你這樣做了,那對于一個論壇來說,同一個帖子被多個人同時回復的可能性是非常高的,那這樣的話,多個人同時回復一個帖子,就會導致多個人同時修改同一個帖子物件,那就導致大家都回復不了,因為會有并發沖突或者資料庫事務的等待超時,因為大家都在修改同一個帖子聚合根;實際上如果我們從業務規則的角度去思考一下,那可以發現,其實帖子和回復之間,只有一個簡單的規則,那就是回復一旦被創建,那他所對應的帖子不能被修改即可;這樣的話,要實作這個規則其實很簡單,把回復作為聚合根,然后把帖子傳入回復聚合根的建構式,然后回復保存帖子ID,然后回復將帖子ID設定為不允許外部修改(private set;即可),這樣我們就實作了這個業務規則,同時還做到了多人同時推一個帖子回復時,不會對同一個帖子物件就并發修改,而是每個回復都是并行的往資料庫插入一潭訓復記錄即可,-- 摘自阿里內部檔案<<關于DDD領域驅動設計中聚合設計的一些思考>>
聚合設計的原則
-
聚合是用來封裝真正的不變性,而不是簡單的將物件組合在一起;
-
-
-
什么是聚合根
聚合根就是聚合的入口,聚合外部只能通過聚合根和聚合內部通信,由于聚合外部只能通過聚合根和聚合內部通信, 這也就意味著外部不能操作除聚合根以外的任何類進行資料庫操作,因為這樣有可能會導致破壞業務的規則,舉個例子,一個汽車四個輪子,如果我們用 Repo 直接操作輪子,對輪子采取delete,而這個時候汽車物件的狀態卻可能是 "running".
如何選出聚合根
1、聚合根一定至少有一個對應的datastore
2、聚合根一定更夠完全描述一組后者多組業務規則(invariant),絕對不會存在一個業務規則需要多個聚合根聯合作用才能做判斷的,
專案/應用的規范設計的長期價值一定是不可忽視的,規范就相當于一個架構內部組件的收納的容器,架構指定了這些組件在邏輯上的組織形式,但而規范則是則是妥妥的物理組織形式,如果沒有合理清晰的規范設計,專案很快就會因為個人開發習慣的不同,導致物理結構混亂,給接下來的應用/專案的維護和擴展產生影響,
放對位置
貼好標簽
下面是我們小團隊參考了阿里COLA框架建議的命名規范做出的開發規范:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
處理request,包括command和query
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
e.g. MetricsRegistrationCmd
|
|
|
|
|
|
e.g. MoMetricsAdminServiceI
|
|
|
如果是無需回傳回傳使用Cola框架的Response,
如果是單個概念回傳使用Cola框架的SingleResponse,
如果是多個概念回傳使用Cola框架的MultiResponse.
如果概念是某個明確的物件,例如MultiResponse<MoMetricsCO>
MultiResponse<ListMetricsValuesResult>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#{Biz}#{userCase}#{scenario}Ext.java
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
使用COLA的擴展框架去實作,COLA的擴展框架的功能不在贅訴,這里討論的是COLA框架的BizScenario的劃分,
BizScenario
private String bizId = "#defaultBizId#";
private String useCase = "#defaultUseCase#";
private String scenario = "#defaultScenario#";
bizId: 業務,對應某個業務訴求, e.g. 新品范訓,
useCase: 業務用例, e.g. 策略定義
scenario:用例下的某個場景,e.g. 定義商家事件任務, 定義小二事件任務
|
|
|
|
|
|
|
|
@Extention(bizId=XXX, userCase=YYY)
|
|
|
@Extention(bizId=XXX, userCase=YYY,scenario=ZZZ)
|
參考:
【1】「技術人生」專題第1篇:什么是技術一號位? :https://www.sohu.com/na/465299450_612370
【2】https://time.geekbang.org/column/article/6605:https://time.geekbang.org/column/article/6605
【3】https://baike.baidu.com/item/%E9%97%AE%E9%A2%98%E5%9F%9F/7181289?fr=aladdin
【4】實作領域驅動設計. 電子工業出版社:https://book.douban.com/subject/25844633/
【5】https://github.com/alibaba/COLA
本文來自博客園,作者:古道輕風,轉載請注明原文鏈接:https://www.cnblogs.com/88223100/p/Understanding-DDD-from-the-perspective-of-software-complexity.html
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/549358.html
標籤:其他
上一篇:【過濾器設計模式詳解】C/Java/JS/Go/Python/TS不同語言實作
下一篇:【過濾器設計模式詳解】C/Java/JS/Go/Python/TS不同語言實作