DDD實施過程中的點滴思考

前一陣段時間鼠渺,陸陸續(xù)續(xù)給幾個客戶開展了領域驅動設計工作坊遵岩。在這個過程中你辣,遇到了客戶提出的各種各樣的問題,出現(xiàn)頻率比較高的是以下幾個尘执。稍微整理了一下舍哄,也加入了自己的一些思考。


如何區(qū)分實體和值對象誊锭?

這可以說是實施DDD時肯定會遇到的老大難問題表悬。基本上所有客戶都會有這個問題丧靡,實體和值對象如何區(qū)分蟆沫?有沒有區(qū)分原則?坦率說窘行,絕對的區(qū)分原則是沒有的饥追,相對普適的我總結了2條:

1.? 實體具有生命周期并且具有明確的業(yè)務含義的狀態(tài)變更

2.? 值對象沒有生命周期,只關心值本身罐盔,不關心值如何變化

其中但绕,原則1是最關鍵的,也是比較難理解的惶看。難就難在很多人搞不清楚怎么才算是具有生命周期并且具有狀態(tài)變更捏顺。很多人會誤把對數(shù)據(jù)庫的增刪改操作當作對象的生命周期與狀態(tài)變更。這兩者其實是完全不相關的兩個東西纬黎。當我們在識別實體和值對象的時候幅骄,我們還在領域建模階段,這個時候是不需要關注具體實現(xiàn)的本今,甚至我都不知道在持久層是否需要使用數(shù)據(jù)庫技術拆座。所以不能簡單地以是否有數(shù)據(jù)庫記錄的新增與刪除操作作為區(qū)分實體和值對象的原則,增刪不是業(yè)務狀態(tài)冠息。很多人建議以是否具有唯一的ID來區(qū)分二者挪凑,認為具有唯一ID就是實體。我個人認為也不是特別好逛艰,因為很多人并不能輕易區(qū)分這里所說的唯一ID與數(shù)據(jù)庫里面的主鍵躏碳。

那怎么才算有生命周期并且存在狀態(tài)變更呢? 最關鍵的是要緊緊抓住業(yè)務含義這個點散怖。 最典型的實體的例子是銀行賬戶菇绵。每個銀行賬戶都有業(yè)務意義上的狀態(tài)肄渗,例如active、inactive咬最、frozen翎嫡、closed等,并且可以在這些狀態(tài)之間變更切換永乌;同時钝的,在一個交易過程當中,賬戶的實例是一直存活著的铆遭,在交易中途并不能銷毀掉然后以另外一個賬戶實例取而代之,即使是一模一樣的實例也不行沿猜。以業(yè)內的術語來說枚荣,這個賬戶是stateful的。

既然實體是stateful的啼肩,值對象自然而然就是stateless橄妆。最簡單的一個例子就是賬戶里面的余額。余額當然會變化祈坠,但是我們并不關心其變化過程害碾,我們只關注當它變化后的值,在一筆交易的過程中赦拘,余額可以隨著業(yè)務邏輯變化慌随,也可以隨時讀取,只要保證變化后的值是正確就ok躺同。

也就是說阁猜,實體和值對象,二者都是對象蹋艺,不同的是剃袍,對于實體來說,我們關注的是對象本身捎谨;對于值對象來說民效,我們關注的是對象所承載的數(shù)據(jù)。


領域服務是什么涛救?

又是一個比較難理解的名詞畏邢。在復雜的業(yè)務中,有時候會遇到一些概念州叠,感覺既不是實體棵红,又不是值對象,它似乎承載了一些行為咧栗,但好像找不到承載所需的對象逆甜。聽起來比較虛虱肄,不過領域服務有一個很突出的特點,就是事件里面的名詞往往也可以作為一個動詞交煞,然后比較難找到一個合適的動詞來充當事件里面的動作咏窿。最典型的例子就是轉賬。轉賬就是一個典型的領域服務素征。轉賬既可以是名詞集嵌,也可以是動詞。當遇到這種情況時御毅,就需要打個心眼根欧,這個概念會不會是個領域服務。不過我們在使用領域服務的時候端蛆,需要特別小心凤粗,我一般在萬不得已的情況下才把一個對象識別為領域服務。因為領域服務過多今豆,容易導致系統(tǒng)模型貧血嫌拣。


如何識別聚合?

首先呆躲,第一步是要識別出聚合根异逐。因為聚合根本質上也是一個實體,所以一般來說我是在識別出實體后再識別聚合根插掂。那怎么識別聚合根呢灰瞻?我的做法是基于實體生命周期的長短來識別。在做事件風暴的時候燥筷,我們是可以分析對象在一個業(yè)務場景下的生命周期長短的箩祥。生命周期相對最長的實體就可以考慮作為這個場景下的聚合根,生命周期被覆蓋的實體作為這個聚合根的實體肆氓,從而形成一個聚合袍祖。舉一個曾經(jīng)實戰(zhàn)的例子。重大故障作戰(zhàn)室與重大故障作戰(zhàn)視頻會議被識別成為重大故障搶修場景下的兩個實體谢揪,二者都具有生命周期蕉陋。但是,重大故障作戰(zhàn)室的生命周期比重大故障作戰(zhàn)視頻會議長拨扶。先有重大故障作戰(zhàn)室凳鬓,后面才會啟動重大故障作戰(zhàn)視頻會議。并且視頻會議會先于作戰(zhàn)室結束患民。重大故障作戰(zhàn)室必須得在重大故障完全修復之后才能結束缩举。所以,在這個場景下,重大故障作戰(zhàn)室就是聚合根仅孩,重大故障作戰(zhàn)視頻會議就是實體托猩,二者形成一個聚合。

然而辽慕,在真實的業(yè)務場景中京腥,有時候會找不到一個實體的生命周期貫穿始終,或者是生命周期間無法完全重合溅蛉,導致不能比較生命周期長短的情況公浪。這個時候,可以考慮拆分兩個聚合根船侧,形成兩個聚合欠气。


場景只有CRUD怎么辦?

坦白說镜撩,如果只有CRUD晃琳,是不需要DDD建模的。但是一個復雜系統(tǒng)里面難免會存在某些小的業(yè)務場景只有CRUD琐鲁。這個時候,在這個場景下往往只有值對象人灼,沒有實體围段,更不用提聚合。這個時候有兩種處理方法投放,第一種是把值對象上升為實體奈泪,相當于無狀態(tài)實體,然后形成聚合灸芳。這往往是安全的涝桅,但反過來,把實體識別為值對象則會破壞模型烙样。第二種處理方法是把值對象融入別的業(yè)務相關性強的聚合冯遂。也有可能意味著業(yè)務場景的拆分不清晰,或者過細谒获,導致做事件風暴的時候場景粒度太小蛤肌。這個時候就需要從新review一下場景拆分的合理性,把過小的場景合并批狱,然后在做事件風暴剑梳。


子域與限界上下文的關系是什么叮雳?

這也是DDD的老大難問題了。基本上每個客戶都會問骂倘。子域是問題域,限屆上下文是解決方案域,子域的問題通過限界上下文解決,就是這個問題的答案砸王。但是這樣說了,客戶就更迷糊了僵芹。坦率說处硬,對這個問題的答案,以及基于這個答案的實施過程拇派,我個人都不覺得滿意荷辕。這個問題難以解釋清楚的根源在于,問題域與解決方案域有時候難以區(qū)分件豌。不同的人從不同的視角出發(fā)疮方,看到的問題與解決方案往往不一樣。

還是以重大故障的例子來說茧彤,從IT的視角出發(fā)骡显,重大故障作戰(zhàn)室就是IT要解決的問題,但是從業(yè)務的視角出發(fā)曾掂,重大故障作戰(zhàn)室就是業(yè)務賴以快速修復重大故障的解決方案惫谤。這個問題就會處于說不清道不明的尷尬境地。所以很多DDD的引導者珠洗,包括我自己在內溜歪,在實施的過程中,都活多或少的刻意回避這個問題许蓖,在得到了解決方案后再反推其想要解決的問題蝴猪。但是坦白講, 我個人對這種做法是持質疑態(tài)度的膊爪。我也曾經(jīng)嘗試使用名詞動詞等方法在事件風暴前從業(yè)務的角度梳理子域自阱,但由于試驗的次數(shù)不多,效果還是需要進一步驗證米酬。


如何劃分限界上下文沛豌?

劃分限界上下文,很重要的一個點是要識別并去除二義性赃额。那什么是二義性呢琼懊?舉個直觀的例子,女兒這個名詞爬早,在不同的家庭所代表的人是不一樣的哼丈。例如在我家,女兒代表的是我的2歲半的女兒筛严;但是在我岳母家醉旦,女兒代表的就是我妻子。我家和我岳母家,就是兩個典型的限界上下文车胡,當說“女兒”的時候檬输,必須明確告知是在哪個家庭的上下文。這就是二義性匈棘。而在實施DDD時丧慈,我一般會在識別出聚合后,進行聚合細化主卫,說白了就是查漏補缺逃默,把事件風暴中遺漏的對象補全,然后鑒別一下是否存在二義性簇搅,如果沒有的話完域,基本上就可以基于聚合之間的關系劃分限界上下文。


能不能基于聚合劃分微服務瘩将?

當然可以吟税。這樣劃分出來的微服務粒度更小,職責更清晰姿现。不好的地方是這種劃分方法可能會導致最后的微服務數(shù)量比較多肠仪,需要考慮微服務治理的成本。


如何識別API备典?

API有兩部分:處理第三方交互的API與處理服務間調用的API藤韵。第三方交互的API可以通過事件風暴中角色為人或者是第三方系統(tǒng)觸發(fā)的命令轉化而來,服務間調用的API可以通過事件風暴中系統(tǒng)內觸發(fā)的命令轉化而來熊经。但因為并不是所有的系統(tǒng)內觸發(fā)的命令都是服務間調用,所有這一步需要結合聚合來識別欲险。


如何進行類方法的設計镐依?

坦白來說,DDD不會設計到類內行為的設計天试。但是槐壳,事件風暴中系統(tǒng)內觸發(fā)的命令,可以轉化為部分類內行為喜每,也就是方法务唐,但不全,需要結合TDD等微觀方法來驅動代碼級的設計带兜。本人覺得DDD + TDD的方法論在銜接上是比較順暢的枫笛,也是值得一試的,宏觀的架構設計與微觀的代碼設計都覆蓋到了刚照。有興趣的同學可以嘗試一下刑巧。

基本上就是這些了。之后在DDD的實施過程中,如果遇到一些新的問題啊楚,會再寫文章分享吠冤。

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市恭理,隨后出現(xiàn)的幾起案子拯辙,更是在濱河造成了極大的恐慌,老刑警劉巖颜价,帶你破解...
    沈念sama閱讀 222,590評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件涯保,死亡現(xiàn)場離奇詭異,居然都是意外死亡拍嵌,警方通過查閱死者的電腦和手機遭赂,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,157評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來横辆,“玉大人撇他,你說我怎么就攤上這事”吩椋” “怎么了困肩?”我有些...
    開封第一講書人閱讀 169,301評論 0 362
  • 文/不壞的土叔 我叫張陵,是天一觀的道長脆侮。 經(jīng)常有香客問我锌畸,道長,這世上最難降的妖魔是什么靖避? 我笑而不...
    開封第一講書人閱讀 60,078評論 1 300
  • 正文 為了忘掉前任潭枣,我火速辦了婚禮,結果婚禮上幻捏,老公的妹妹穿的比我還像新娘盆犁。我一直安慰自己,他們只是感情好篡九,可當我...
    茶點故事閱讀 69,082評論 6 398
  • 文/花漫 我一把揭開白布谐岁。 她就那樣靜靜地躺著,像睡著了一般榛臼。 火紅的嫁衣襯著肌膚如雪伊佃。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,682評論 1 312
  • 那天沛善,我揣著相機與錄音航揉,去河邊找鬼。 笑死金刁,一個胖子當著我的面吹牛迷捧,可吹牛的內容都是我干的织咧。 我是一名探鬼主播,決...
    沈念sama閱讀 41,155評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼漠秋,長吁一口氣:“原來是場噩夢啊……” “哼笙蒙!你這毒婦竟也來了?” 一聲冷哼從身側響起庆锦,我...
    開封第一講書人閱讀 40,098評論 0 277
  • 序言:老撾萬榮一對情侶失蹤捅位,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后搂抒,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體艇搀,經(jīng)...
    沈念sama閱讀 46,638評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,701評論 3 342
  • 正文 我和宋清朗相戀三年求晶,在試婚紗的時候發(fā)現(xiàn)自己被綠了焰雕。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,852評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡芳杏,死狀恐怖矩屁,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情爵赵,我是刑警寧澤吝秕,帶...
    沈念sama閱讀 36,520評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站空幻,受9級特大地震影響烁峭,放射性物質發(fā)生泄漏。R本人自食惡果不足惜秕铛,卻給世界環(huán)境...
    茶點故事閱讀 42,181評論 3 335
  • 文/蒙蒙 一约郁、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧但两,春花似錦鬓梅、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,674評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽士袄。三九已至悲关,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間娄柳,已是汗流浹背寓辱。 一陣腳步聲響...
    開封第一講書人閱讀 33,788評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留赤拒,地道東北人秫筏。 一個月前我還...
    沈念sama閱讀 49,279評論 3 379
  • 正文 我出身青樓诱鞠,卻偏偏與公主長得像,于是被迫代替她去往敵國和親这敬。 傳聞我的和親對象是個殘疾皇子航夺,可洞房花燭夜當晚...
    茶點故事閱讀 45,851評論 2 361

推薦閱讀更多精彩內容