DDD的意義
關(guān)注精簡的業(yè)務(wù)模型及實現(xiàn)的匹配浮入,模型是對現(xiàn)實的有選著性的抽象和精簡抓于。
通過模型噩凹、通用語言,來跟領(lǐng)域?qū)<遥I(yè)務(wù)人員)毡咏、開發(fā)人員等進(jìn)行信息溝通驮宴。
通過將一個復(fù)雜的系統(tǒng)抽象建模和拆分成一個個子系統(tǒng)(模塊、子域)呕缭,以達(dá)到減少系統(tǒng)復(fù)雜度的目的堵泽,一個簡單的系統(tǒng)、或者一個初創(chuàng)的系統(tǒng)暫時沒必要恢总,也沒有意義使用DDD進(jìn)行設(shè)計及構(gòu)建迎罗,同時考慮人員因素。
軟件是將現(xiàn)實世界中的事務(wù)映射為計算機(jī)可執(zhí)行的代碼片仿,以期待提高我們的生產(chǎn)效率纹安,DDD告訴我們?nèi)绾屋^好的完成這樣的轉(zhuǎn)化,以實現(xiàn)較好的高內(nèi)聚低耦合的原則。
名詞解釋
1. 領(lǐng)域
即業(yè)務(wù)厢岂、是相關(guān)業(yè)務(wù)所涉及到的業(yè)務(wù)知識光督,如飛行系統(tǒng)、廣告行業(yè)塔粒,消費(fèi)金融行業(yè)结借、打車行業(yè)等。
2. 模型
將現(xiàn)實事物進(jìn)行抽象卒茬,保留所涉及系統(tǒng)業(yè)務(wù)關(guān)注的屬性船老、行為,去除多余的屬性和行為后的結(jié)果圃酵,比如金融行業(yè)中的用戶柳畔,我們關(guān)注用戶年齡、身份證信息等郭赐,不關(guān)注用戶有多高荸镊,多重。
3. 通用語言(UL)
在由軟件架構(gòu)師堪置、開發(fā)人員、領(lǐng)域?qū)<医M成的設(shè)計團(tuán)隊张惹,需要有一種語言來統(tǒng)一它們的行為舀锨。以幫助它們創(chuàng)建一個模型,并使用代碼來表達(dá)模型宛逗。有點類似java的中間字節(jié)碼坎匿,串聯(lián)起開發(fā)文本代碼與可執(zhí)行二進(jìn)制文件。
(a). 以業(yè)務(wù)(領(lǐng)域)名詞為基準(zhǔn)雷激,開發(fā)使用相同概念替蔬。
(b). 對業(yè)務(wù)名詞添加限制和約束,定義子域和上下文(邊界)屎暇。
(c). 隨著業(yè)務(wù)的發(fā)展承桥,創(chuàng)建名詞庫,便于后續(xù)同學(xué)理解根悼。
(d). 通用語言凶异,可以以圖、表挤巡、UML剩彬、草圖等形式呈現(xiàn)出來。最重要的是讓業(yè)務(wù)人員(不懂技術(shù))矿卑、系統(tǒng)設(shè)計人員喉恋、開發(fā)人員(可能不懂業(yè)務(wù))對系統(tǒng)(模塊)劃分,上下文邊界達(dá)成共識。
4. 模型驅(qū)動設(shè)計
通常以面向?qū)ο蟮姆绞絹韺崿F(xiàn)轻黑。在領(lǐng)域抽象設(shè)計通用語言中糊肤,用于指導(dǎo)開發(fā)人員具體實現(xiàn)的思想。介紹如何來劃分子領(lǐng)域苔悦,讓子領(lǐng)域能夠落地實現(xiàn)轩褐。
模型驅(qū)動設(shè)計
1. 系統(tǒng)分層(Layered Architecture)
任何一個復(fù)雜系統(tǒng),都不可能不進(jìn)行分層設(shè)計。因為人的思維習(xí)慣一次性記住不了這么多東西病瞳,需要分層簡化處理整份。分層設(shè)計比較典型的場景應(yīng)該屬于TCP/IP的分層設(shè)計。
對到業(yè)務(wù)開發(fā)來說拗踢,通常我們的系統(tǒng)設(shè)計如下:
(a).用戶界面層:負(fù)責(zé)用戶側(cè)展示與交互,絕大部分場景下向臀,系統(tǒng)設(shè)計都是前后端分離巢墅,該模塊交由前端處理。
(b).應(yīng)用層:很薄的一層券膀,用來完成系統(tǒng)流程組合(聚合)君纫,不涉及具體領(lǐng)域內(nèi)容(邏輯)。通常是跟前端交互的最直接一層芹彬。
(c).領(lǐng)域?qū)?/strong>:用來完成具體領(lǐng)域(業(yè)務(wù))邏輯蓄髓、信息、對象處理舒帮,也是業(yè)務(wù)系統(tǒng)的核心所在会喝。在該層,重業(yè)務(wù)邏輯玩郊,輕存儲肢执。隨著業(yè)務(wù)的復(fù)雜度提升,該層也許進(jìn)一步進(jìn)行模塊化拆分译红。
(d).基礎(chǔ)設(shè)施層:作為基礎(chǔ)層预茄,輕業(yè)務(wù)邏輯,重存儲侦厚,完成模型的存儲(db/緩存)反璃、外部系統(tǒng)調(diào)用(rpc)等。
對到我以前做的金融系統(tǒng)分層如下:
網(wǎng)關(guān)層:對應(yīng)用戶界面層假夺,完成跟前端或rpc 消費(fèi)者交互淮蜈。
調(diào)度層:對應(yīng)應(yīng)用層,完成接口流程聚合已卷、處理MQ事件梧田、以及部分定時腳本觸發(fā)流程。
邏輯層:對應(yīng)具體業(yè)務(wù)邏輯處理。
數(shù)據(jù)層:完成數(shù)據(jù)的持久化裁眯、以及rpc訪問封裝鹉梨。
了解到廣告創(chuàng)編服務(wù)化相關(guān)系統(tǒng)分層如下:
2. 實體(Entities)
模型分實體或值對象。實體是整個系統(tǒng)設(shè)計中重要的對象穿稳,該對象通常具有唯一標(biāo)識符存皂,通常也是該領(lǐng)域設(shè)計總的重要模型。
比如銀行系統(tǒng)中客戶(具有客戶id逢艘,身份證號)旦袋、廣告系統(tǒng)中的廣告(具有廣告ID)、交易系統(tǒng)中的訂單(具有訂單號)等它改。
3. 值對象(Value Objects)
我們不可能將所有的對象都定義成實體(每個都分配唯一標(biāo)識符疤孕,成本過高),因此有些不是那么重要的央拖,也不需要關(guān)注狀態(tài)的對象祭阀,可以定義成值對象。
如一個坐標(biāo)定義Point(x1,x2)鲜戒、一個地址定義Address(國家专控、城市、街道)遏餐。
4. 服務(wù)(Service)
服務(wù)是模型設(shè)計對外暴露行為或調(diào)用的單元伦腐,服務(wù)不同于對象,通常包含一系列行為(操作)境输。它具備如下特點:
(a). 服務(wù)的操作包含領(lǐng)域概念,無法映射成一個對象颖系。
(b). 操作無狀態(tài)嗅剖。
5. 模塊(Module)
對于一個大型的復(fù)雜項目,模型(業(yè)務(wù))會越來越大嘁扼,每個人理解起來都會相對比較困難信粮,根據(jù)人的思維習(xí)慣,通常需要進(jìn)行系統(tǒng)拆分趁啸,分開理解降低系統(tǒng)復(fù)雜度强缘,拆分過程中,將具有公共屬性的對象不傅、行為組裝在一個模塊中旅掂。模塊名稱需要成為通用語言理解的一部分。
如金融支付系統(tǒng)中的模塊劃分:將大的模塊劃分為通道接入访娶、核心交由商虐、資金接入。
廣告創(chuàng)編服務(wù)化相關(guān)模塊劃分如下:
6. 聚合(Aggregates)
聚合,可以簡單理解為對象關(guān)聯(lián)秘车,將相關(guān)聯(lián)的對象聚合在一個實體(聚合根)下面典勇,外界對該組對象的訪問,都只能通過聚合根訪問叮趴。下圖例子比較形象:
消費(fèi)者對象(實體割笙、聚合根),關(guān)聯(lián)了聯(lián)系簿對象眯亦,和地址對象伤溉,外界訪問通過消費(fèi)者對象訪問。
7. 工廠(Factories)
工廠被用來封裝對象創(chuàng)建所必需的知識搔驼,創(chuàng)建對象可以由對象自己來封裝谈火,也可以由第三方來創(chuàng)建。
創(chuàng)建型模式:工廠方法模式舌涨、抽象工廠模式糯耍、單例模式、建造者模式囊嘉、原型模式
8. 資源庫(Repositories)
資源庫温技,完成對象的持久化存儲、遠(yuǎn)程RPC調(diào)用封裝扭粱。
保持模型的一致性(各個模塊/領(lǐng)域如何保持協(xié)作)
我們現(xiàn)在已經(jīng)有了各個模塊舵鳞、子領(lǐng)域,如何確保不同子領(lǐng)域如何協(xié)作琢蛤。
1. 界定的上下文(Bounded Context)
當(dāng)我們確定一個模型的時候蜓堕,我們需要確定它的范圍,定出它的上下文的邊界博其,盡最大可能保持模型的統(tǒng)一套才。上下文的界定通常可以從如下幾個方面確定:
(a). 團(tuán)隊的組織結(jié)構(gòu)慕淡。
(b). 應(yīng)用的特定部分中的慣例背伴。
(c). 物理表現(xiàn)(例如代碼倉庫、數(shù)據(jù)庫峰髓、redis集群等)
2. 持續(xù)集成(Continuous Intergration)
持續(xù)集成指的是傻寂,頻繁地(一天多次)將代碼集成到主干。持續(xù)集成的目的携兵,就是讓產(chǎn)品可以快速迭代疾掰,同時還能保持高質(zhì)量。它的核心措施是徐紧,代碼集成到主干之前个绍,必須通過自動化測試勒葱。
(1)快速發(fā)現(xiàn)錯誤。每完成一點更新巴柿,就集成到主干凛虽,可以快速發(fā)現(xiàn)錯誤,定位錯誤也比較容易广恢。
(2)防止分支大幅偏離主干凯旋。如果不是經(jīng)常集成,主干又在不斷更新钉迷,會導(dǎo)致以后集成的難度變大至非,甚至難以集成。
3. 上下文映射(Context Map)
各個模塊如何進(jìn)行協(xié)調(diào)的手段糠聪。不同上下文之間的交互荒椭。
(1). 共享內(nèi)核(Shared Kernel)
有點類似于多線程操作共同變量,通過共享內(nèi)存進(jìn)行交互舰蟆。兩個不同領(lǐng)域模型(團(tuán)隊)趣惠,共享部分領(lǐng)域模型子集(模塊),任何對子集的操作都需要知會到其他共享的領(lǐng)域模型(團(tuán)隊)身害,集成的時候味悄,兩個團(tuán)隊都需要開發(fā)和測試介入。
曾經(jīng)跟另外一個業(yè)務(wù)團(tuán)隊共用了一個底層額度(功能)開發(fā)塌鸯,每次涉及公共模塊開發(fā)都需要知會另一個團(tuán)隊侍瑟,這樣做有些問題,溝通效率也不高丙猬,后來改成有一個團(tuán)隊維護(hù)公共模塊涨颜,另一個團(tuán)隊如果有述求,提需求給維護(hù)的團(tuán)隊處理茧球。涉及到下面的模式(客戶-供應(yīng)商)庭瑰。
(2). 客戶-供應(yīng)商(Customer Supplier Teams)
當(dāng)一個系統(tǒng)嚴(yán)重依賴于另一個子系統(tǒng),兩個系統(tǒng)的上下文是不同的袜腥,并且一個系統(tǒng)的處理結(jié)果被作為另外一個的輸入见擦。一個系統(tǒng)如果有述求钉汗,提需求給下游子系統(tǒng)羹令。
(3). 順從模式(Conformist)
上游上下文只能盲目依賴下游上下文。
通常损痰,可以通過MQ福侈、數(shù)據(jù)庫主從復(fù)制,做到隔離卢未。
(4). 開放主機(jī)服務(wù)(Open Host Service)
定義一種協(xié)議肪凛,讓其他上下文來訪問本上下文堰汉。
曾經(jīng),我們的各個業(yè)務(wù)系統(tǒng)(很多子領(lǐng)域)需要訪問中臺訂單系統(tǒng)(中臺服務(wù))伟墙,中臺不可能為每個業(yè)務(wù)系統(tǒng)單獨定制化訪問接口翘鸭,因此提煉出一套通用的標(biāo)準(zhǔn)訂單查詢接口(Published Language),定義好入?yún)⒚杜e值,出參枚舉值戳葵,訪問系統(tǒng)token等信息就乓。各個業(yè)務(wù)系統(tǒng)共用一套訪問接口,減少溝通拱烁、維護(hù)成本生蚁。
(5). 隔離通道(Separate Way)
當(dāng)多個系統(tǒng),沒有任何交集的模型(上下文)的時候戏自,這些系統(tǒng)可以獨立拆分獨立建模邦投,無需集成,各自玩各自的擅笔。
(6). 防腐層(Anticorruption Layer)
上游上下文(模型)依賴于下游上下文(模型)志衣,為避免下游上下文嵌入過深,導(dǎo)致下游有調(diào)整剂娄,上游調(diào)整比較大蠢涝,在中間,上游上下文引入適配層阅懦,隔離下游變化和二,訪問下游系統(tǒng)。
4. 提煉(找到重心)
即使在我們改進(jìn)和創(chuàng)建很多抽象之后耳胎,一個大的領(lǐng)域還是會有一個很大模型惯吕,我們需要進(jìn)行提煉和子域劃分,在子域中怕午,提煉出一個核心域(Core Domain)废登,若干個普通子域(Generic Subdomain)。
核心域需投入較多的精力進(jìn)行優(yōu)化和迭代郁惜。
對于普通域來說堡距,有如下幾種方式:
(1). 購買現(xiàn)成的解決方案
這個方法的好處是可以使用別人已經(jīng)完成的全套解決方案。隨之而來的是學(xué)習(xí)曲線的問題兆蕉,而且這樣的方案還會引入一些依賴羽戒。如果代碼有很多 bug,你只得等待別人來解決虎韵。
(2). 外包
將設(shè)計和實現(xiàn)交給另外一個團(tuán)隊易稠,有可能是其他公司的團(tuán)隊。這樣做可以使你專注于核心域包蓝,不再承受處理另一個領(lǐng)域的負(fù)擔(dān)驶社。不便的地方是集成外包的代碼企量。
(3). 已有模型
一個取巧的方案是使用一個已經(jīng)創(chuàng)建的模型。市面上已經(jīng)有一些關(guān)于分析模式的書亡电,可以用來作為我們子域的靈感來源届巩。
(4). 自己實現(xiàn)
這個方案的好處是能夠做到最好的集成,但這也意味著額外的付出份乒,包括維護(hù)的壓力等姆泻。
DDD來講述軟件設(shè)計的術(shù)與器,本質(zhì)是為了高內(nèi)聚低耦合冒嫡。
關(guān)于DDD的思考與疑問
領(lǐng)域驅(qū)動設(shè)計拇勃,是先有業(yè)務(wù)(領(lǐng)域),再有設(shè)計孝凌。針對未知的環(huán)境或領(lǐng)域方咆,是否還合適(比如創(chuàng)業(yè)團(tuán)隊)?
重構(gòu)蟀架,隨著業(yè)務(wù)的發(fā)展需要持續(xù)進(jìn)行瓣赂?
參考
領(lǐng)域驅(qū)動設(shè)計精簡版(全新修訂):https://www.infoq.cn/article/domain-driven-design-quickly-new
阿里技術(shù)專家詳解DDD系列 第二彈 - 應(yīng)用架構(gòu):https://mp.weixin.qq.com/s/MU1rqpQ1aA1p7OtXqVVwxQ
領(lǐng)域驅(qū)動設(shè)計在美團(tuán)點評業(yè)務(wù)系統(tǒng)的實踐:https://mp.weixin.qq.com/s/uZmrZ61jB2JERCqHNDqktQ
重讀領(lǐng)域驅(qū)動設(shè)計——如何說好一門通用語言:https://zhuanlan.zhihu.com/p/62959217
23種設(shè)計模式匯總整理:https://blog.csdn.net/jason0539/article/details/44956775
持續(xù)集成是什么?:http://www.ruanyifeng.com/blog/2015/09/continuous-integration.html