領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)系列文章(3)——有選擇性的使用領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)
本系列的第一篇博文拋磚引玉,大談?lì)I(lǐng)域驅(qū)動(dòng)設(shè)計(jì)的優(yōu)勢(shì),這里筆者還是希望以客觀的態(tài)度,談?wù)勵(lì)I(lǐng)域驅(qū)動(dòng)設(shè)計(jì)的缺點(diǎn)及其不適合使用的場(chǎng)景,以讓讀者可以有選擇性的使用領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)。
我們知道,沒有最好,只有最合適,設(shè)計(jì)也是一樣。因此,所謂設(shè)計(jì),就是以你和你的團(tuán)隊(duì)的知識(shí)、經(jīng)驗(yàn)和智慧,全面充分的考慮各種內(nèi)外因素后,在你們的設(shè)計(jì)方案中作出合理的選擇的過程。而這些影響你們選擇的因素主要有:
-
技術(shù)框架的特征和約束(如果你的項(xiàng)目決定使用C語言進(jìn)行開發(fā),那么首先在設(shè)計(jì)方法上,就需要使用面向過程而非面向?qū)ο蟮脑O(shè)計(jì)方法)。
-
時(shí)間的壓力和約束(你永遠(yuǎn)不可能告訴你的老板,給我10年時(shí)間,我和我的團(tuán)隊(duì)將為你設(shè)計(jì)出世界上最優(yōu)秀的軟件)。
- 你的團(tuán)隊(duì)的能力、經(jīng)驗(yàn)、性格、價(jià)值觀等因素的約束(你不能期望一個(gè)長(zhǎng)期從事遺留系統(tǒng)維護(hù)項(xiàng)目或大部分成員是缺乏經(jīng)驗(yàn)的高校畢業(yè)生的團(tuán)隊(duì)能很好的按照你的設(shè)計(jì)意圖去實(shí)現(xiàn)你的高度抽象的優(yōu)秀設(shè)計(jì),同時(shí)你也別指望一幫家里經(jīng)濟(jì)條件不錯(cuò),本著過來熬時(shí)間的家伙會(huì)樂意與你一起刻苦鉆研、精益求精)。
-
你的系統(tǒng)的特征(如果你想把一個(gè)足夠簡(jiǎn)單而且在可以預(yù)計(jì)的將來都不存在很大規(guī)模的需求變更的系統(tǒng)設(shè)計(jì)得很復(fù)雜,很精妙,具有很好的擴(kuò)展性,但要為此付出巨大的時(shí)間、人力成本,這顯然是一種不理智的過度設(shè)計(jì)(Over design))。
- 其他外在因素的約束(你的項(xiàng)目需要參與投標(biāo),你必須壓縮人力、時(shí)間等以讓你的項(xiàng)目成本成為巨大的競(jìng)爭(zhēng)資本)。
當(dāng)然,上述的考慮因素站在比較高的角度,通常是項(xiàng)目經(jīng)理、架構(gòu)師需要考慮的問題,但這當(dāng)中你應(yīng)該會(huì)得到一些啟發(fā)?;氐轿覀兊闹黝},我們首先看看,領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)相對(duì)于傳統(tǒng)的面向過程式的設(shè)計(jì),有什么缺點(diǎn):
- 復(fù)雜化:面向過程思維之所以一直很受歡迎,是因?yàn)樗苤庇^,非常符合大部分人的思維習(xí)慣,大部分人拿到一個(gè)問題,通常都是會(huì)很直觀的想第一步做什么、第二步做什么,如果怎樣,應(yīng)該怎樣,否則怎樣……,可以說,任何水平的程序員,都能很好的使用面向過程的方法進(jìn)行設(shè)計(jì)和開發(fā)。同時(shí),由于我們教育水平的落后和整體IT環(huán)境的制約,可以這樣說,真正掌握面向?qū)ο笏季S和設(shè)計(jì)方法的程序員的比例非常低(雖然絕大部分都使用面向?qū)ο蟮恼Z言和工具),而本身面向?qū)ο笏季S要求人有很好的抽象思維能力,因?yàn)槟阈枰岩粋€(gè)復(fù)雜的系統(tǒng)一層層的抽象為簡(jiǎn)單的部分,需要把現(xiàn)實(shí)世界的事物(有些是可見的,但有些是不可見)合理的抽象為計(jì)算機(jī)世界的不同元素。這些都不是一些很容易做的事情,要做得好,就更難。
- 團(tuán)隊(duì)的抗拒:如果你的團(tuán)隊(duì)(很大可能)大部分人都習(xí)慣于用很直觀的面向過程的方式進(jìn)行設(shè)計(jì)和開發(fā),你需要推動(dòng)你的團(tuán)隊(duì)轉(zhuǎn)換思維來采用面向?qū)ο蟮姆绞竭M(jìn)行領(lǐng)域驅(qū)動(dòng)設(shè)計(jì),通常會(huì)遭到多數(shù)人的抗拒。因?yàn)槿硕际怯卸栊缘模麄兞?xí)慣安于現(xiàn)狀,而改變是痛苦的,他們要付出額外的努力,需要進(jìn)行學(xué)習(xí),但以筆者的經(jīng)驗(yàn),相當(dāng)一部分程序員,特別是有一定工作年限的程序員,他們從事IT工作都只是為了獲得一份不錯(cuò)的報(bào)酬,因此他們的學(xué)習(xí)動(dòng)力非常有限,而且,他們都相當(dāng)自負(fù),被說服的難度比較大。
- 管理、開發(fā)和維護(hù)的成本高:復(fù)雜度更高,意味著你需要花更多的時(shí)間進(jìn)行設(shè)計(jì),同時(shí)需要花出額外的時(shí)間進(jìn)行必要的培訓(xùn),而且需要有更完善的文檔(設(shè)計(jì)文檔,API文檔,培訓(xùn)文檔等)。領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)的抽象程度比較高,因此必需有良好的文檔,否則,隨著項(xiàng)目的不斷迭代、升級(jí)和維護(hù),它很容易因?yàn)楹髞碚叩恼`解而慢慢回歸面向過程的設(shè)計(jì),甚至?xí)兊?#8220;四不象”,領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)的成本優(yōu)勢(shì)是隨著時(shí)間的推移慢慢體現(xiàn)的(見下圖),如果出現(xiàn)這種情況,所有前面付出的努力都會(huì)付諸東流。

系統(tǒng)的初始階段,領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)需要付出更大的成本,但隨著時(shí)間的推移,領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)的成本效益優(yōu)勢(shì)會(huì)逐步顯現(xiàn)
- 性能比較低:使用純面向?qū)ο蟮姆绞竭M(jìn)行領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)的程序,其系統(tǒng)開銷通常要比面向過程設(shè)計(jì)的程序高,從而性能相對(duì)較低(關(guān)于具體的例子,后續(xù)的博文會(huì)提及)。
那么,假設(shè)我們?cè)跁r(shí)間、團(tuán)隊(duì)能力及各種資源都允許的情況下,是否就可以麻木的全盤使用領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)呢?正如本博文的標(biāo)題一樣,答案是否定的,我們需要有選擇性的使用。讓我們來看看一些指導(dǎo)性原則:
- 使用領(lǐng)域驅(qū)動(dòng)設(shè)計(jì),并不代表整個(gè)系統(tǒng)的方方面面都必須遵從領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)的原則,需要根據(jù)實(shí)際情況,讓適合的部分使用領(lǐng)域驅(qū)動(dòng)設(shè)計(jì),讓不適合的部分使用面向過程的設(shè)計(jì)。
- 對(duì)于那些業(yè)務(wù)規(guī)則非常簡(jiǎn)單,通常只有增、刪、改、查的簡(jiǎn)單操作,而且也不大可能發(fā)生大規(guī)模需求變更的模塊,可以讓業(yè)務(wù)實(shí)體成為一個(gè)“貧血模型”,例如一些基礎(chǔ)數(shù)據(jù):系統(tǒng)參數(shù)、商品類型、國(guó)家、地址信息(注:對(duì)于這點(diǎn),本人持保留態(tài)度,因?yàn)檫@些業(yè)務(wù)雖然非常簡(jiǎn)單,但既然選擇了領(lǐng)取驅(qū)動(dòng)設(shè)計(jì),即使把這些業(yè)務(wù)實(shí)體設(shè)計(jì)為“充血模型”,即把極其簡(jiǎn)單的業(yè)務(wù)邏輯也封裝在業(yè)務(wù)實(shí)體中,也并不比使用“貧血模型”成本高,而它卻帶來了統(tǒng)一設(shè)計(jì)風(fēng)格的好處)。
- 對(duì)于查詢操作,特別是復(fù)雜的查詢操作,出于性能的考慮,可以用結(jié)構(gòu)化查詢邏輯一次性完成,并把這些邏輯封裝在Repository(即技術(shù)上的DAO)中(這方面的具體例子,后面關(guān)于“查詢通道”和“領(lǐng)域?qū)ο髠}庫”的博文會(huì)更具體的闡述)。我們可以看到,對(duì)于一些業(yè)務(wù)視圖,以及報(bào)表模塊,很明顯不適合使用面向?qū)ο蟮姆绞皆O(shè)計(jì),因?yàn)檫@些“視圖”和“報(bào)表”,本質(zhì)上就不是業(yè)務(wù)實(shí)體。
- 同樣出于性能的考慮,在業(yè)務(wù)實(shí)體的實(shí)現(xiàn)邏輯中,某些操作不適合過度偏執(zhí)的使用面向?qū)ο蠓绞?。例如,?#8220;訂單”的“新增訂單明細(xì)”(order.addOrderItem(orderItem))中,如果業(yè)務(wù)邏輯規(guī)定一張訂單中包含優(yōu)惠商品的明細(xì)數(shù)目不能超過20條,使用面向?qū)ο蟮姆绞?,就需要把訂單中的所有訂單明?xì)全部加載,然后逐個(gè)明細(xì)判斷其對(duì)應(yīng)的商品是否優(yōu)惠商品,再統(tǒng)計(jì)出優(yōu)惠商品的數(shù)目,這樣很明顯是低效率和高開銷的,這里只需要使用Repository提供的一個(gè)統(tǒng)計(jì)方法,用一個(gè)結(jié)構(gòu)化查詢邏輯返回統(tǒng)計(jì)結(jié)果即可,而這就是非面向?qū)ο蟮姆绞健?/span>
本博文給有志于領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)的讀者潑了一下冷水,提出一些“反模式”(Bitter),是為了讓讀者冷靜一下,在領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)過程中作出更靈活和更合理的選擇。關(guān)于這方面的論述,筆者在這里淺嘗則止,限于水平、經(jīng)驗(yàn)和表達(dá)能力,不敢胡亂賣弄,建議讀者可以參考閱讀Martin Fowler的《Patterns of Enterprise Application Architecture》一書的相關(guān)觀點(diǎn)。