序言
領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)(Domain-Driven Design,DDD)提出距今已經(jīng)有 20 年的歷史鞍陨,雖然前十多年的時(shí)間都一直處于不溫不火的狀態(tài)噪猾,但一直在頑強(qiáng)的生長(zhǎng)磕秤。最近幾年,微服務(wù)大行其道臭笆,DDD 也開始普及了起來(lái)叙淌。我們完全可以斷定,是微服務(wù)的熱風(fēng)讓研發(fā)人員重新發(fā)現(xiàn)了 DDD 的價(jià)值愁铺,再加上敏捷軟件開發(fā)和 DevOps 的發(fā)展為 DDD 的落地鋪好了道路鹰霍,DDD 終于咸魚翻身,星火燎原茵乱∶鳎可以說(shuō),微服務(wù)就像是 DDD 的心上人瓶竭,使得 DDD 真正煥發(fā)起了青春督勺。
微服務(wù)架構(gòu)從提出以來(lái)一直沒(méi)有很好的理論支撐如何合理地劃分服務(wù)邊界,研發(fā)人員常常為服務(wù)要?jiǎng)澐侄啻蠖鵂?zhēng)吵不休斤贰,而 DDD 被發(fā)現(xiàn)恰好可以彌補(bǔ)微服務(wù)的營(yíng)養(yǎng)不良:
- 服務(wù)最大不要大過(guò)一個(gè)限界上下文(Bounded Context)玷氏,否則服務(wù)內(nèi)可能會(huì)存在有歧義的領(lǐng)域概念;
- 服務(wù)最小不要小過(guò)一個(gè)聚合(Aggregate)腋舌,否則會(huì)引入分布式事務(wù)的復(fù)雜度盏触;
- 服務(wù)間最好通過(guò)領(lǐng)域事件(Domain Event)來(lái)進(jìn)行交互,這樣可以讓服務(wù)保持松耦合。
微服務(wù)與 DDD 的結(jié)合赞辩,讓微服務(wù)架構(gòu)看起來(lái)似乎更加穩(wěn)健了雌芽。
近年來(lái),學(xué)習(xí) DDD 的研發(fā)人員越來(lái)越多辨嗽,很多學(xué)員反饋 DDD 很神秘世落,不容易掌握。本文嘗試揭開 DDD 的神秘面紗糟需,包括下面七層:
- DDD 發(fā)展了 OO屉佳;
- DDD 發(fā)展了敏捷;
- 統(tǒng)一語(yǔ)言洲押;
- 戰(zhàn)略設(shè)計(jì)武花;
- 戰(zhàn)術(shù)設(shè)計(jì);
- 分層架構(gòu)杈帐;
- 兩關(guān)聯(lián)一循環(huán)体箕。
面紗一:DDD 發(fā)展了 OO
在標(biāo)準(zhǔn)的 UML(Unified Modeling Language)過(guò)程中,軟件研發(fā)被明確的分為面向?qū)ο蠓治觯∣OA)挑童,面向?qū)ο笤O(shè)計(jì)(OOD)和面向?qū)ο缶幋a(OOP)階段:
- 在 OOA 階段累铅,分析師(領(lǐng)域?qū)<一驑I(yè)務(wù)專家)以需求為輸入,完全從業(yè)務(wù)視角出發(fā)站叼,對(duì)需求中的領(lǐng)域概念進(jìn)行分析和提煉娃兽,并定義它們之間的關(guān)系,建立分析模型尽楔;
- 在 OOD 階段换薄,設(shè)計(jì)師(架構(gòu)師)以分析模型為輸入,從技術(shù)視角出發(fā)翔试,補(bǔ)充被分析師忽略但卻與軟件開發(fā)效率息息相關(guān)的因素(軟硬件平臺(tái)轻要、數(shù)據(jù)存儲(chǔ)約束、并發(fā)垦缅、編程框架...)冲泥,進(jìn)一步按照軟件工程的要求(各種設(shè)計(jì)原則和模式)重塑了分析師給出的分析模型,建立設(shè)計(jì)模型壁涎;
- 在 OOP 階段凡恍,程序員(開發(fā)人員)根據(jù)設(shè)計(jì)模型來(lái)編寫代碼,并根據(jù)代碼的反饋來(lái)演進(jìn)設(shè)計(jì)模型怔球。
分析師是業(yè)務(wù)人員(業(yè)務(wù)方)嚼酝,而設(shè)計(jì)師和程序員都是技術(shù)人員(技術(shù)方)。OOA 方法雖然在理論上是完備的竟坛,但在實(shí)踐中卻常常會(huì)有問(wèn)題闽巩。由于業(yè)務(wù)人員在 OOA 階段缺少技術(shù)視角考慮钧舌,那么分析模型在轉(zhuǎn)化為設(shè)計(jì)模型的時(shí)候,可能會(huì)很難實(shí)現(xiàn)涎跨,需要通過(guò)復(fù)雜的轉(zhuǎn)換才能適應(yīng)軟件開發(fā)的要求洼冻,因此兩種模型就有了比較大的 gap。
在之后的開發(fā)過(guò)程中隅很,程序員往往只是聚焦于設(shè)計(jì)模型撞牢,而將分析模型束之高閣。一方從任何研發(fā)活動(dòng)中獲得的知識(shí)都無(wú)法流暢的傳遞給另一方叔营,導(dǎo)致分析模型與設(shè)計(jì)模型之間的差異越來(lái)越大屋彪,分析模型變得天馬行空不著邊際,也就失去了存在的意義绒尊。
許多系統(tǒng)的真正復(fù)雜之處并不在于技術(shù)畜挥,而在于領(lǐng)域本身和業(yè)務(wù)用戶及其執(zhí)行的業(yè)務(wù)活動(dòng)。如果在軟件設(shè)計(jì)時(shí)沒(méi)有獲得對(duì)領(lǐng)域的深刻理解垒酬,沒(méi)有通過(guò)模型將復(fù)雜的領(lǐng)域邏輯以模型概念和模型元素的形式清晰地表達(dá)出來(lái)砰嘁,那么無(wú)論我們使用多么先進(jìn)件炉、多么流行的基礎(chǔ)設(shè)施勘究,都難以保證項(xiàng)目的真正成功。
DDD 拋棄了將分析模型與設(shè)計(jì)模型分離的做法斟冕,而是尋找單個(gè)模型來(lái)滿足兩方面的要求口糕,這就是領(lǐng)域模型。領(lǐng)域模型是業(yè)務(wù)視角和技術(shù)視角的交集磕蛇,它反映了業(yè)務(wù)人員和技術(shù)人員的共識(shí)景描。對(duì)領(lǐng)域模型的建立,必須由業(yè)務(wù)人員和技術(shù)人員達(dá)成一致秀撇。隨著領(lǐng)域模型的演進(jìn)及精煉超棺,技術(shù)實(shí)現(xiàn)越來(lái)越逼近業(yè)務(wù)的本質(zhì),軟件系統(tǒng)可以真實(shí)反映業(yè)務(wù)需求呵燕,且易于理解和維護(hù)棠绘。
面紗二:DDD 發(fā)展了敏捷
在敏捷軟件開發(fā)中,團(tuán)隊(duì)更傾向于被定義為一個(gè)個(gè)獨(dú)立的特性團(tuán)隊(duì)再扭。在特性團(tuán)隊(duì)內(nèi)部氧苍,業(yè)務(wù)專家、架構(gòu)師泛范、開發(fā)人員和測(cè)試人員要能夠無(wú)障礙的溝通让虐,并集體為特性的端到端交付負(fù)責(zé)。一種被普遍接受的觀點(diǎn)是“軟件源代碼是唯一真正的設(shè)計(jì)產(chǎn)物”罢荡,一些偏重技術(shù)實(shí)踐的敏捷軟件開發(fā)方法(如 XP)極大的推動(dòng)了這方面的實(shí)踐:技術(shù)人員優(yōu)先將精力投入到代碼上赡突,持續(xù)保持代碼的清晰和靈活性对扶,通過(guò)重構(gòu)代碼來(lái)演進(jìn)設(shè)計(jì)模型,并通過(guò)自動(dòng)化測(cè)試來(lái)確保設(shè)計(jì)演進(jìn)的正確性和安全性麸俘。
技術(shù)人員通過(guò)代碼不斷修改實(shí)際的業(yè)務(wù)模型辩稽,但這種變化很難有效傳遞給業(yè)務(wù)人員,于是這種變化就成為了“隱秘的角落”从媚。同時(shí)逞泄,也很難讓業(yè)務(wù)人員以技術(shù)人員改變后的業(yè)務(wù)概念去思考問(wèn)題。最終導(dǎo)致雙方互看不爽拜效,分歧越來(lái)越大喷众。因此,看起來(lái)匪夷所思的情況紧憾,實(shí)際上一直都在發(fā)生到千。技術(shù)人員一直都在定義業(yè)務(wù)赤嚼,只是沒(méi)有合理的途徑讓業(yè)務(wù)人員了解并接納而已物咳。
領(lǐng)域模型從觀念上改變了這一狀況,它將大家從各自的領(lǐng)地措拇,也就是業(yè)務(wù)與技術(shù)中般眉,拉到了一個(gè)中間地帶了赵。領(lǐng)域模型既不完全屬于業(yè)務(wù),也不完全屬于技術(shù)甸赃,而是雙方共有的部分柿汛。于是技術(shù)人員與業(yè)務(wù)人員就有了差不多的話語(yǔ)權(quán),至少有了可以溝通和協(xié)同的空間埠对。
一旦業(yè)務(wù)人員接受了領(lǐng)域模型络断,實(shí)際上就是放棄了對(duì)業(yè)務(wù) 100% 的控制權(quán),也意味著領(lǐng)域模型能夠賦予技術(shù)人員定義業(yè)務(wù)的權(quán)利项玛。但領(lǐng)域模型在給技術(shù)人員帶來(lái)額外的權(quán)利同時(shí)貌笨,也隱含著額外的義務(wù),即技術(shù)人員在領(lǐng)域模型的實(shí)現(xiàn)過(guò)程中襟沮,要接受業(yè)務(wù)人員的監(jiān)督與反饋锥惋。如果業(yè)務(wù)人員不同意技術(shù)人員對(duì)領(lǐng)域模型的修改,那么技術(shù)人員需要盡快修復(fù)這種誤差臣嚣,就是說(shuō)技術(shù)人員喪失了對(duì)代碼 100% 的控制權(quán)净刮,也意味著領(lǐng)域模型能夠賦予業(yè)務(wù)人員影響軟件實(shí)現(xiàn)的權(quán)利。讓業(yè)務(wù)人員與技術(shù)人員參與到對(duì)方的工作中硅则,可以打破雙方的知識(shí)壁壘淹父,知識(shí)傳遞與溝通的效率就變得更高,反饋速度就變得更快怎虫,同時(shí)浪費(fèi)也變得更少暑认,所以說(shuō)這是一種更好的協(xié)同方式困介。
DDD 發(fā)展了敏捷,它顯式地把領(lǐng)域和設(shè)計(jì)放到了軟件研發(fā)的核心蘸际,業(yè)務(wù)人員和技術(shù)人員被得到同樣的重視座哩,他們通過(guò)協(xié)同來(lái)構(gòu)建并演進(jìn)領(lǐng)域模型,這讓敏捷軟件開發(fā)方法真正的在領(lǐng)域知識(shí)復(fù)雜的行業(yè)內(nèi)得以有效應(yīng)用粮彤。
面紗三:統(tǒng)一語(yǔ)言
一般情況下根穷,業(yè)務(wù)人員嘴里全是業(yè)務(wù),技術(shù)人員嘴里全是技術(shù)导坟。業(yè)務(wù)人員對(duì)問(wèn)題域的理解很深刻屿良,但卻不太關(guān)注解決方案域,而技術(shù)人員對(duì)解決方案域非常熟悉惫周,但對(duì)問(wèn)題域的理解卻不充分尘惧。隨著業(yè)務(wù)的發(fā)展和變化,雙方之間就逐步形成了一道人為的鴻溝递递。
統(tǒng)一語(yǔ)言(Ubiquitous Language)喷橙,也叫通用語(yǔ)言,是業(yè)務(wù)人員和技術(shù)人員共同創(chuàng)建的一套語(yǔ)言登舞,必須在團(tuán)隊(duì)范圍內(nèi)達(dá)成一致贰逾。團(tuán)隊(duì)所有成員使用統(tǒng)一語(yǔ)言進(jìn)行交流,每個(gè)人都能聽懂別人在說(shuō)什么逊躁。顯然似踱,創(chuàng)建統(tǒng)一語(yǔ)言就是定義團(tuán)隊(duì)成員的“普通話”隅熙,團(tuán)隊(duì)所有角色在腦海中對(duì)領(lǐng)域知識(shí)生成的畫面是高度一致的稽煤,如下圖所示:
統(tǒng)一語(yǔ)言,將技術(shù)人員的思考起點(diǎn)拉到了業(yè)務(wù)而不是技術(shù)上囚戚,也就是從問(wèn)題出發(fā)酵熙,這就在一定程度上填平了那道人為的鴻溝。
業(yè)務(wù)人員和技術(shù)人員如何建立統(tǒng)一語(yǔ)言驰坊?最簡(jiǎn)單的做法就是讓業(yè)務(wù)人員和開發(fā)人員一起匾二,找一塊白板,把各種概念都寫在上面拳芙,然后整理出領(lǐng)域名詞察藐、領(lǐng)域動(dòng)詞和領(lǐng)域規(guī)則。這里面的重點(diǎn)是舟扎,讓業(yè)務(wù)人員和開發(fā)人員在一起分飞。如果只讓一方出現(xiàn),結(jié)果又會(huì)是原來(lái)的樣子睹限,因?yàn)槟銢](méi)法判斷譬猫,這里面的語(yǔ)言對(duì)方是否聽得懂讯檐。這種做法很簡(jiǎn)單,但通常都不夠系統(tǒng)染服,會(huì)存在各種遺漏别洪。2013 年,一位叫 Alberto 的 DDD 專家提出了一種更正式的實(shí)踐——事件風(fēng)暴(Event Storming)柳刮,這種方法簡(jiǎn)單易學(xué)挖垛,充分體現(xiàn)了 DDD 中溝通協(xié)作和統(tǒng)一語(yǔ)言等要點(diǎn),所以逐漸開始流行起來(lái)秉颗。
事件風(fēng)暴主要分為三步:
- 第一步是把識(shí)別領(lǐng)域事件晕换。領(lǐng)域事件表示的是業(yè)務(wù)流程中每個(gè)步驟引發(fā)的結(jié)果。事件風(fēng)暴的作者認(rèn)為站宗,從結(jié)果入手來(lái)梳理需求闸准,比從操作入手,更容易把業(yè)務(wù)想清楚梢灭。事件風(fēng)暴中的“事件”兩個(gè)字就來(lái)源于領(lǐng)域事件夷家。領(lǐng)域事件的命名,如果套用英語(yǔ)的語(yǔ)法來(lái)說(shuō)敏释,一般是完成時(shí) + 被動(dòng)語(yǔ)態(tài)库快。比如說(shuō),訂單已提交钥顽,這個(gè)“已”字就是完成時(shí)义屏,代表已經(jīng)發(fā)生的事情。而訂單已提交也可以說(shuō)成訂單已“被”提交蜂大,實(shí)際是被動(dòng)語(yǔ)態(tài)闽铐,只不過(guò)一般把被字給省掉了。除過(guò)識(shí)別領(lǐng)域事件以外奶浦,也要將發(fā)現(xiàn)的領(lǐng)域規(guī)則寫在便利貼上進(jìn)行備忘兄墅,并貼在對(duì)應(yīng)的領(lǐng)域事件的下方。
- 第二步是識(shí)別命令澳叉。所謂命令就是引發(fā)領(lǐng)域事件的操作隙咸,我們可以通過(guò)分析領(lǐng)域事件得到,將命令貼到領(lǐng)域事件的上方成洗。除了識(shí)別出命令本身以外五督,我們通常還要識(shí)別出誰(shuí)執(zhí)行的命令,以及為了執(zhí)行命令我們要查詢出什么數(shù)據(jù)瓶殃,將執(zhí)行者和查詢數(shù)據(jù)貼到命令的上方充包。對(duì)于執(zhí)行者,可以是人(其實(shí)是人扮演的角色)碌燕,也可以是其他系統(tǒng)误证,還可以是定時(shí)器继薛。
- 第三步是識(shí)別聚合。先挪動(dòng)上一步的便利貼愈捅,把圍繞同一個(gè)領(lǐng)域名詞的命令遏考、事件、執(zhí)行者蓝谨、查詢數(shù)據(jù)擺在一起灌具,分成好幾“堆”,然后把領(lǐng)域名詞寫在大一點(diǎn)的便利貼上譬巫,貼在每堆便利貼的中間咖楣,這就將圍繞同一個(gè)領(lǐng)域名詞的所有元素聚集在一起,形成聚合芦昔。這里的聚合僅僅是領(lǐng)域名詞诱贿,不同于 DDD 中的聚合。領(lǐng)域名詞有可能只是 DDD 聚合充當(dāng)?shù)慕巧径校蛘呤?DDD 聚合的屬性珠十,還有些領(lǐng)域名詞需要經(jīng)過(guò)合并或拆解后,才是合理的 DDD 聚合凭豪,而這些需要等到領(lǐng)域建模時(shí)才能真正搞清楚焙蹭。
事件風(fēng)暴不僅能幫助我們挖掘領(lǐng)域需求,盡量把遺漏的需求補(bǔ)充完全嫂伞,而且還能以協(xié)作的方式保證業(yè)務(wù)人員和技術(shù)人員對(duì)需求理解一致孔厉。一定要注意,“協(xié)作”才是事件風(fēng)暴的精髓帖努,而具體結(jié)果怎樣呈現(xiàn)撰豺,反而是第二位的。
事件風(fēng)暴結(jié)束后然磷,我們要將過(guò)程中建立的統(tǒng)一語(yǔ)言以表格的形式記錄下來(lái)郑趁,包括領(lǐng)域名詞刊驴、領(lǐng)域動(dòng)詞和領(lǐng)域規(guī)則姿搜。
領(lǐng)域名詞記錄舉例:
名詞(英文/縮寫) | 名詞(中文) | 解釋 | 生命周期 | 關(guān)系 |
---|---|---|---|---|
fund | 資金 | 賬戶中以貨幣表示的資產(chǎn) | 開始時(shí)創(chuàng)建,銷戶時(shí)注銷 | 歸屬于賬戶(1:1) |
領(lǐng)域動(dòng)詞記錄舉例:
動(dòng)詞(英文/縮寫) | 動(dòng)詞(中文) | 解釋 | 主語(yǔ) | 賓語(yǔ) |
---|---|---|---|---|
transfer to | 轉(zhuǎn)賬 | 將資金從一個(gè)賬戶轉(zhuǎn)到另一個(gè)賬戶 | 爸爸 | 女兒 |
領(lǐng)域規(guī)則記錄舉例:
規(guī)則編號(hào) | 規(guī)則描述 | 舉例 | 影響的主要功能 |
---|---|---|---|
R001 | 單筆轉(zhuǎn)賬限額為 N 萬(wàn)元 | 當(dāng) N 為 50萬(wàn)元時(shí):a.如果爸爸給女兒?jiǎn)喂P轉(zhuǎn)賬 60 萬(wàn)元捆憎,則轉(zhuǎn)賬失敗舅柜,錯(cuò)誤為超過(guò)單筆轉(zhuǎn)賬限額; b.如果爸爸給女兒?jiǎn)喂P轉(zhuǎn)賬 50 萬(wàn)元,則轉(zhuǎn)賬成功 | 轉(zhuǎn)賬 |
有了統(tǒng)一語(yǔ)言后躲惰,我們就可以構(gòu)建領(lǐng)域模型了致份。領(lǐng)域模型是統(tǒng)一語(yǔ)言的軟件模型,需要通過(guò)領(lǐng)域建模得到础拨,在 DDD 中對(duì)應(yīng)模型驅(qū)動(dòng)設(shè)計(jì)(Model-Driven Design)氮块,分為戰(zhàn)略設(shè)計(jì)和戰(zhàn)術(shù)設(shè)計(jì)兩部分绍载。
面紗四:戰(zhàn)略設(shè)計(jì)
戰(zhàn)略設(shè)計(jì),這個(gè)名詞看起來(lái)有點(diǎn)高大上滔蝉,是模型驅(qū)動(dòng)設(shè)計(jì)的高層設(shè)計(jì)击儡,而戰(zhàn)術(shù)設(shè)計(jì)則是低層設(shè)計(jì)。如果不以戰(zhàn)略設(shè)計(jì)開始蝠引,戰(zhàn)術(shù)設(shè)計(jì)將無(wú)法被有效實(shí)施阳谍。在展開具體實(shí)現(xiàn)細(xì)節(jié)之前,需要優(yōu)先完成宏觀層面的戰(zhàn)略設(shè)計(jì)螃概,它強(qiáng)調(diào)的是業(yè)務(wù)戰(zhàn)略上的重點(diǎn)矫夯,如何按重要性分配工作,以及如何進(jìn)行最佳整合吊洼。
一提起戰(zhàn)略設(shè)計(jì)训貌,很多人就會(huì)想起子域(Subdomain)、限界上下文(Bounded Context冒窍,BC)和上下文映射(Context Mapping)等模式旺订,其中子域是為了將不同的業(yè)務(wù)區(qū)分開來(lái),也就是要將識(shí)別出來(lái)的業(yè)務(wù)概念做一個(gè)劃分超燃,而限界上下文和上下文映射是將劃分出來(lái)的業(yè)務(wù)落實(shí)到真實(shí)的解決方案中区拳。
領(lǐng)域建模首先是一個(gè)定義問(wèn)題的方法,其次才是解決問(wèn)題的方法意乓。就是說(shuō)樱调,一方面,我們要知道解決的問(wèn)題是什么届良;另一方面笆凌,我們要知道怎么去解決問(wèn)題。
我們要解決的問(wèn)題就是領(lǐng)域問(wèn)題士葫,在 DDD 中乞而,通過(guò)子域模式先把問(wèn)題從大面上進(jìn)行分解。軟件研發(fā)是解決問(wèn)題慢显,而解決問(wèn)題要分而治之爪模。所謂分而治之,就是要把問(wèn)題分解了荚藻,對(duì)應(yīng)到 DDD 中屋灌,就是要把一個(gè)大領(lǐng)域分解成若干個(gè)小領(lǐng)域,而這個(gè)分解出來(lái)的小領(lǐng)域就是子域应狱。
通過(guò)事件風(fēng)暴共郭,我們建立了統(tǒng)一語(yǔ)言,而統(tǒng)一語(yǔ)言對(duì)應(yīng)著模型。在戰(zhàn)略設(shè)計(jì)中除嘹,我們要對(duì)這些統(tǒng)一語(yǔ)言做個(gè)分類写半,把它們劃分到不同的子域中去。比如我們要做一個(gè)代碼打靶服務(wù)尉咕,首先要把打靶和靶場(chǎng)管理這兩件事分開污朽,一個(gè)是打靶人員的打靶活動(dòng),另一個(gè)是代碼專家的內(nèi)容管理龙考。同時(shí)蟆肆,用戶登陸和分權(quán)分域也是要考慮的基本問(wèn)題,那么身份管理也可以作為一個(gè)子域晦款。類似的炎功,還有數(shù)據(jù)服務(wù)和智能學(xué)習(xí)等子域。
對(duì)于一個(gè)真實(shí)項(xiàng)目而言缓溅,劃分出來(lái)的子域可能會(huì)有很多蛇损,但并非每個(gè)子域都一樣重要。所以坛怪,我們還要把劃分出來(lái)的子域再做一下區(qū)分淤齐,分成核心域(Core Domain)、支撐域(Supporting Subdomain)和通用域(Generic Subdomain)袜匿。
核心域是整個(gè)系統(tǒng)最重要的部分更啄,是整個(gè)業(yè)務(wù)得以成功的關(guān)鍵。關(guān)于核心域居灯,Eric 曾提出過(guò)幾個(gè)問(wèn)題祭务,幫我們識(shí)別核心域:
- 為什么這個(gè)系統(tǒng)值得寫?
- 為什么不直接買一個(gè)怪嫌?
- 為什么不外包义锥?
如果你對(duì)這幾個(gè)問(wèn)題的回答能夠幫你找到這個(gè)服務(wù)或系統(tǒng)非寫不可的理由,那它就是你的核心域岩灭。對(duì)于代碼打靶服務(wù)來(lái)說(shuō)拌倍,打靶子域和靶場(chǎng)管理子域是核心域。
有一些子域不是你的核心競(jìng)爭(zhēng)力噪径,但卻是系統(tǒng)不得不做的東西柱恤,市場(chǎng)上也找不到一個(gè)現(xiàn)成的方案,這種子域就是支撐域熄云。對(duì)于代碼打靶服務(wù)來(lái)說(shuō)膨更,打靶人員的能力畫像、弱項(xiàng)分析和成長(zhǎng)曲線等是和打靶結(jié)果密切相關(guān)的缴允,同時(shí)要針對(duì)性的建設(shè)知識(shí)庫(kù),并進(jìn)行智能推薦,讓打靶人員更好更快的提升 Code Review 能力和編碼能力练般,這是對(duì)代碼打靶服務(wù)核心能力擴(kuò)展的重要一步矗漾,所以智能學(xué)習(xí)子域就是一個(gè)支撐域。
還有一種子域叫通用域薄料,就是行業(yè)里通常都是這么做敞贡,即便不自己做,也并不影響你的業(yè)務(wù)運(yùn)行摄职。對(duì)于代碼打靶服務(wù)來(lái)說(shuō)誊役,數(shù)據(jù)服務(wù)提供度量看板,包括組織看板和個(gè)人看板谷市,行業(yè)里有現(xiàn)成的做法蛔垢,所以數(shù)據(jù)服務(wù)子域就是一個(gè)通用域。
我們之所以要區(qū)分不同的子域迫悠,關(guān)鍵的原因就在于鹏漆,我們可以決定不同的投資策略。核心域要全力投入创泄,支撐域次之艺玲,通用域甚至可以花錢買服務(wù)。
通過(guò)劃分子域鞠抑,區(qū)分核心域饭聚、支撐域和通用域,我們把 DDD 在問(wèn)題層面的概念已經(jīng)說(shuō)清楚了搁拙。接下來(lái)若治,就要進(jìn)入到解決方案層面了。我們現(xiàn)在有了切分出來(lái)的子域感混,怎樣去落實(shí)到代碼上呢端幼?首先要解決的就是這些子域如何組織的問(wèn)題,是寫一個(gè)程序把所有子域都放在里面呢弧满,還是每個(gè)子域做一個(gè)獨(dú)立的應(yīng)用婆跑,抑或是有一些在一起,有一些分開庭呜。這就引出了領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)中的一個(gè)重要的概念滑进,限界上下文。
限界上下文募谎,顧名思義扶关,它形成了一個(gè)邊界,一個(gè)限定了通用語(yǔ)言自由使用的邊界数冬,一旦出界节槐,含義便無(wú)法保證搀庶。關(guān)于限界上下文的劃分,需要同時(shí)考慮業(yè)務(wù)邊界铜异、工作邊界和技術(shù)邊界哥倔,筆者之前寫了一篇文章《聊聊服務(wù)化架構(gòu)的邊界》,感興趣的讀者可以進(jìn)一步閱讀揍庄。
有了對(duì)限界上下文的理解咆蒿,我們就可以把整個(gè)業(yè)務(wù)分解到不同的限界上下文中,但是蚂子,盡管我們拆分了系統(tǒng)沃测,它們終究還是一個(gè)系統(tǒng),免不了彼此之間要有交互食茎。所以蒂破,我們就要有一種描述方式,將不同限界上下文之間交互的方式描述出來(lái)董瞻,這就是上下文映射寞蚌。DDD 給我們提供了一些描述這種交互的方式,比如:
- 合作關(guān)系(Partnership)钠糊;
- 共享內(nèi)核(Shared Kernel)挟秤;
- 客戶-供應(yīng)商(Customer-Supplier);
- 跟隨者(Conformist)抄伍;
- 防腐層(Anticorruption Layer)艘刚;
- 開放主機(jī)服務(wù)(Open Host Service);
- 發(fā)布語(yǔ)言(Published Language)截珍;
- 各行其道(Separate Ways)攀甚;
- 大泥球(Big Ball of Mud)。
之所以有這么多不同的交互方式岗喉,主要是為了讓你在頭腦中仔細(xì)辨認(rèn)一下秋度,看看限界上下文之間到底在以怎樣的方式進(jìn)行交互。
在定義好不同的限界上下文钱床,將它們之間的交互通過(guò)上下文映射呈現(xiàn)出來(lái)之后荚斯,我們就得到了一張上下文映射圖(Context Map)。上下文映射圖是可以幫助我們理解系統(tǒng)的各個(gè)部分之間查牌,是怎樣進(jìn)行交互的事期,幫我們建立一個(gè)全局性的認(rèn)知,而這往往是很多團(tuán)隊(duì)欠缺的纸颜。
面紗五:戰(zhàn)術(shù)設(shè)計(jì)
對(duì)于戰(zhàn)術(shù)設(shè)計(jì)兽泣,就是要得到一個(gè)具體的模型,這個(gè)模型是關(guān)于統(tǒng)一語(yǔ)言的軟件模型胁孙,稱作領(lǐng)域模型唠倦。經(jīng)過(guò)戰(zhàn)略設(shè)計(jì)称鳞,我們已經(jīng)將統(tǒng)一語(yǔ)言拆分到不同的限界上下文中了,那么領(lǐng)域模型就不是單一的牵敷、內(nèi)聚的和全功能式的模型胡岔,而是存在于限界上下文這個(gè)顯式的邊界之內(nèi)法希。
領(lǐng)域模型通過(guò)領(lǐng)域模型圖表達(dá)枷餐,一般用 UML 類圖來(lái)畫:
我們可以通過(guò)對(duì)象組合來(lái)表達(dá)更復(fù)雜的業(yè)務(wù)概念,同時(shí)通過(guò)對(duì)象泛化來(lái)表達(dá)更抽象的業(yè)務(wù)概念苫亦。建立領(lǐng)域模型毛肋,主要是識(shí)別領(lǐng)域?qū)ο螅㈩I(lǐng)域?qū)ο笾g的關(guān)系屋剑,以及確定領(lǐng)域?qū)ο蟮年P(guān)鍵屬性润匙,必要的時(shí)候還要將領(lǐng)域?qū)ο蠼M織成模塊。
如果你想把 UML 學(xué)的更深入一點(diǎn)唉匾,可以讀一讀《UML 用戶指南》和《UML 精粹》孕讳。
對(duì)于領(lǐng)域?qū)ο螅珼DD 中主要包括實(shí)體(Entity)和值對(duì)象(Value Object)巍膘。實(shí)體指的是能夠通過(guò)唯一標(biāo)識(shí)符標(biāo)識(shí)出來(lái)的對(duì)象厂财,有生命周期管理,而值對(duì)象僅僅表示一個(gè)值峡懈。實(shí)體的屬性是可以變的璃饱,只要標(biāo)識(shí)符不變,它就還是那個(gè)實(shí)體肪康。但值對(duì)象的屬性卻不能變荚恶,一旦變了,它就不再是那個(gè)對(duì)象磷支,所以谒撼,我們會(huì)把值對(duì)象設(shè)置成一個(gè)不變的對(duì)象。
在 DDD 中我們?yōu)槭裁匆獙㈩I(lǐng)域?qū)ο蠓譃閷?shí)體和值對(duì)象雾狈?其實(shí)主要是為了分出值對(duì)象廓潜,也就是把變的對(duì)象和不變的對(duì)象區(qū)分開。
我們通常會(huì)花很大的精力來(lái)區(qū)分實(shí)體和值對(duì)象箍邮,那么得到的值對(duì)象能帶來(lái)什么好處呢茉帅?這就需要了解值對(duì)象有什么優(yōu)點(diǎn)。關(guān)于值對(duì)象锭弊,優(yōu)點(diǎn)主要體現(xiàn)在內(nèi)存和數(shù)據(jù)庫(kù)布局的靈活性上堪澎。有了這種靈活性,就可以根據(jù)性能和編程方便性等因素味滞,決定值對(duì)象的不同實(shí)現(xiàn)方式樱蛤。其次钮呀,值對(duì)象的不變性也會(huì)帶來(lái)更高的程序質(zhì)量。這些優(yōu)點(diǎn)昨凡,都是實(shí)體不具備的爽醋。
接下來(lái),我們將注意力轉(zhuǎn)移到領(lǐng)域?qū)ο蟮纳芷诠芾恚?/p>
- 使用工廠(Factory)模式來(lái)創(chuàng)建和銷毀領(lǐng)域?qū)ο螅?/li>
- 使用聚合(Aggregate)模式來(lái)封裝領(lǐng)域?qū)ο螅?/li>
- 使用倉(cāng)儲(chǔ)(Repository)來(lái)查找和持久化領(lǐng)域?qū)ο蟆?/li>
工廠和倉(cāng)儲(chǔ)理解起來(lái)一點(diǎn)都不難便脊,我們重點(diǎn)看一下聚合蚂四。
聚合就是多個(gè)實(shí)體或值對(duì)象的組合,它們共同構(gòu)成了一個(gè)業(yè)務(wù)邊界哪痰。聚合里可以包含很多個(gè)對(duì)象遂赠,每個(gè)對(duì)象里還可以繼續(xù)包含其它的對(duì)象,就像一棵大樹一層層展開晌杰。但重點(diǎn)是跷睦,這是一棵樹,所以肋演,它只能有一個(gè)樹根抑诸,這個(gè)根就是聚合根(Aggregate Root)。聚合根必須是一個(gè)實(shí)體爹殊,是從外部訪問(wèn)這個(gè)聚合的起點(diǎn)蜕乡。可見边灭,最簡(jiǎn)單的聚合僅包含一個(gè)實(shí)體异希。
有了聚合模式后,我們所說(shuō)的領(lǐng)域?qū)ο蟠蠖鄶?shù)情況下特指的是聚合绒瘦,也有時(shí)指的是聚合內(nèi)部的實(shí)體或值對(duì)象称簿,這個(gè)可以通過(guò)所在的上下文來(lái)判斷。
在 DDD 中惰帽,關(guān)于聚合設(shè)計(jì)有四條基本規(guī)則:
- 在聚合邊界內(nèi)保護(hù)業(yè)務(wù)規(guī)則不變性憨降;
- 聚合要設(shè)計(jì)得小巧;
- 只能通過(guò)標(biāo)識(shí)符引用其他聚合该酗;
- 使用最終一致性更新其他聚合授药。
聚合最基本的作用,是為一組具有整體部分關(guān)系的領(lǐng)域?qū)ο缶S護(hù)不變規(guī)則呜魄。當(dāng)我們掌握了這種建模技術(shù)以后悔叽,還可以發(fā)現(xiàn)其他一些層面的作用:
- 聚合不僅是“被動(dòng)地”實(shí)現(xiàn)不變規(guī)則,它還為我們提供了一個(gè)新的視角爵嗅,可以更細(xì)致地和業(yè)務(wù)人員討論業(yè)務(wù)規(guī)則娇澎。
- 技術(shù)人員過(guò)去一般認(rèn)為事務(wù)只是一個(gè)技術(shù)概念。現(xiàn)在我們可以看到睹晒,事務(wù)其實(shí)是來(lái)源于業(yè)務(wù)規(guī)則的趟庄,本質(zhì)上是個(gè)業(yè)務(wù)問(wèn)題括细。也就是說(shuō),聚合在業(yè)務(wù)規(guī)則和事務(wù)之間建立了起聯(lián)系戚啥。
- 我們?cè)谀P蜕峡梢詾槊總€(gè)聚合建一個(gè)包奋单,可以認(rèn)為,聚合是一種特殊的模塊猫十。這樣览濒,模型的層次就變得更清晰了。同時(shí)炫彩,我們也可以把聚合當(dāng)作一個(gè)粗粒度的概念單位進(jìn)行思考匾七,降低了認(rèn)知負(fù)載絮短。
如果限界上下文中聚合比較多江兢,還可以再通過(guò)模塊(Module)模式對(duì)聚合進(jìn)行分組,進(jìn)一步降低認(rèn)知負(fù)載丁频。
面紗六:分層架構(gòu)
分層架構(gòu)的一個(gè)重要原則是每層只能與位于其下方的層發(fā)生耦合杉允。分層架構(gòu)可以簡(jiǎn)單分為兩種,即嚴(yán)格分層架構(gòu)和松散分層架構(gòu)席里。在嚴(yán)格分層架構(gòu)中叔磷,某層只能與位于其直接下方的層發(fā)生耦合,而在松散分層架構(gòu)中奖磁,則允許某層與它的任意下方層發(fā)生耦合改基。
分層架構(gòu)的好處是顯而易見的:
- 首先,由于層間松散的耦合關(guān)系咖为,使得我們可以專注于本層的設(shè)計(jì)秕狰,而不必關(guān)心其他層的設(shè)計(jì),也不必?fù)?dān)心自己的設(shè)計(jì)會(huì)影響其它層躁染,對(duì)提高軟件質(zhì)量大有裨益鸣哀。
- 其次,分層架構(gòu)使得程序結(jié)構(gòu)清晰吞彤,升級(jí)和維護(hù)都變得十分容易我衬,更改某層的具體實(shí)現(xiàn)代碼,只要本層的接口保持穩(wěn)定饰恕,其他層可以不必修改挠羔。即使本層的接口發(fā)生變化,也只影響相鄰的上層埋嵌,修改工作量小且錯(cuò)誤可以控制破加,不會(huì)帶來(lái)意外的風(fēng)險(xiǎn)。
DDD 中強(qiáng)調(diào)領(lǐng)域模型對(duì)應(yīng)的代碼應(yīng)該是被嚴(yán)格隔離出來(lái)的莉恼,并保持與模型的高度一致性拌喉。在限界上下文中速那,推薦使用分層架構(gòu)或者解耦更純粹的六邊形架構(gòu),將領(lǐng)域模型和其它支撐代碼(UI尿背,DB端仰,通訊等)進(jìn)行解耦,可以凸顯領(lǐng)域模型田藐,有效地管理業(yè)務(wù)復(fù)雜度和技術(shù)復(fù)雜度荔烧。
六邊形每條不同的邊代表了不同類型的端口,端口要么處理輸入汽久,要么處理輸出鹤竭。對(duì)于每種外界類型,都有一個(gè)適配器與之對(duì)應(yīng)景醇,外界通過(guò)應(yīng)用層 API 與內(nèi)部進(jìn)行交互臀稚。上圖中有 3 個(gè)客戶請(qǐng)求均抵達(dá)相同的輸入端口(適配器 A、B 和 C)三痰,另一個(gè)客戶請(qǐng)求使用了適配器 D 吧寺。假設(shè)前 3 個(gè)請(qǐng)求使用了 HTTP 協(xié)議(瀏覽器、REST 和 SOAP 等)散劫,而后一個(gè)請(qǐng)求使用了 AMQP 協(xié)議(比如 RabbitMQ)稚机。端口并沒(méi)有明確的定義,它是一個(gè)非常靈活的概念获搏。無(wú)論采用哪種方式對(duì)端口進(jìn)行劃分赖条,當(dāng)客戶請(qǐng)求到達(dá)時(shí),都應(yīng)該有相應(yīng)的適配器對(duì)輸入進(jìn)行轉(zhuǎn)化常熙,然后端口將調(diào)用應(yīng)用程序的某個(gè)操作或者向應(yīng)用程序發(fā)送一個(gè)事件纬乍,控制權(quán)由此交給內(nèi)部區(qū)域。
應(yīng)用程序通過(guò)公共 API 接收客戶請(qǐng)求症概,使用領(lǐng)域模型來(lái)處理請(qǐng)求蕾额。我們可以將倉(cāng)儲(chǔ)的實(shí)現(xiàn)看作是持久化適配器,該適配器用于訪問(wèn)先前存儲(chǔ)的聚合實(shí)例或者保存新的聚合實(shí)例彼城。正如圖中的適配器 E诅蝶、F 和 G 所展示的,我們可以通過(guò)不同的方式實(shí)現(xiàn)資源庫(kù)募壕,比如關(guān)系型數(shù)據(jù)庫(kù)调炬、基于文檔的存儲(chǔ)、分布式緩存或內(nèi)存存儲(chǔ)等舱馅。如果應(yīng)用程序向外界發(fā)送領(lǐng)域事件消息缰泡,我們將使用適配器 H 進(jìn)行處理。該適配器處理消息輸出,而上面提到的處理 AMQP 消息的適配器則是處理消息輸入的棘钞,因此應(yīng)該使用不同的端口缠借。
我們?cè)趯?shí)際的項(xiàng)目開發(fā)中,不同層的組件可以同時(shí)開發(fā)宜猜。當(dāng)一個(gè)組件的功能明確后泼返,就可以立即啟動(dòng)開發(fā)。由于該組件的用戶有多個(gè)姨拥,并且這些用戶的側(cè)重點(diǎn)不同绅喉,所以需要提供多個(gè)不同的接口。同時(shí)叫乌,這些用戶的認(rèn)識(shí)也是不斷深入的柴罐,可能會(huì)多次重構(gòu)相關(guān)的接口。于是憨奸,組件的多個(gè)用戶經(jīng)常會(huì)找組件的開發(fā)者討論這些問(wèn)題革屠,無(wú)形中降低了組件的開發(fā)效率。
我們換一種方式膀藐,組件的開發(fā)者在明確了組件的功能后就專注于功能的開發(fā)屠阻,確保功能穩(wěn)定和高效。組件的用戶自己定義組件的接口(端口)额各,然后基于接口寫測(cè)試,并不斷演進(jìn)接口吧恃。在跨層集成測(cè)試時(shí)虾啦,由組件開發(fā)者或用戶再開發(fā)一個(gè)適配器就可以了。
在 DDD 分層架構(gòu)中痕寓,比較難區(qū)分的是領(lǐng)域服務(wù)和應(yīng)用服務(wù)傲醉,兩者都是無(wú)狀態(tài)的。
在面向?qū)ο蟮脑O(shè)計(jì)中呻率,有貧血模型和充血模型之分硬毕,簡(jiǎn)單來(lái)說(shuō):
- 對(duì)于貧血模型,對(duì)象中僅有數(shù)據(jù)沒(méi)有行為礼仗,偏向過(guò)程式編程范式吐咳;
- 對(duì)于充血模型,對(duì)象中既有數(shù)據(jù)又有行為元践,偏向面向?qū)ο缶幊谭妒健?/li>
如果使用貧血模型片酝,則領(lǐng)域?qū)ο蟮男袨槎紩?huì)放到領(lǐng)域服務(wù)中舞骆,這時(shí)領(lǐng)域服務(wù)就比較多;如果使用充血模型,則領(lǐng)域?qū)ο蟮男袨槎紩?huì)封裝在內(nèi)部篡诽,這時(shí)領(lǐng)域服務(wù)就比較少,僅僅包括不適合放到領(lǐng)域?qū)ο笾械臉I(yè)務(wù)行為,比如轉(zhuǎn)賬業(yè)務(wù)行為涉及兩個(gè)領(lǐng)域?qū)ο螅粋€(gè)是源賬戶琅豆,一個(gè)是目標(biāo)賬戶。
領(lǐng)域?qū)臃庋b的邏輯通常是細(xì)粒度的篓吁,并不適合直接作為 API 暴露給外部趋距。另外,還有一些不屬于領(lǐng)域?qū)拥臋M切關(guān)注點(diǎn)越除,比如像事務(wù)控制节腐,應(yīng)該單獨(dú)處理。所以摘盆,我們往往要在領(lǐng)域?qū)油饷嬖偌右粚右砣福珼DD 分層架構(gòu)和六邊形架構(gòu)都將這一層稱為應(yīng)用層。 應(yīng)用層本身并不包含領(lǐng)域邏輯孩擂,而是對(duì)領(lǐng)域?qū)又械倪壿嬤M(jìn)行封裝和編排狼渊。
應(yīng)用層作為領(lǐng)域?qū)拥摹伴T面”,把領(lǐng)域?qū)臃庋b成更粗粒度的服務(wù)供外部使用类垦,并且處理事務(wù)和日志等橫切關(guān)注點(diǎn)狈邑。應(yīng)用層的應(yīng)用服務(wù)是必須有的,對(duì)應(yīng)一個(gè)具體的用戶故事蚤认,具有業(yè)務(wù)價(jià)值米苹。但領(lǐng)域?qū)拥念I(lǐng)域服務(wù)可以沒(méi)有,除非有跨領(lǐng)域?qū)ο蟮臉I(yè)務(wù)行為封裝砰琢。在應(yīng)用服務(wù)中蘸嘶,可以直接調(diào)用聚合的接口進(jìn)行領(lǐng)域?qū)ο蟮膭?chuàng)建、查詢和持久化等陪汽,無(wú)需在中間再封裝一層冗余的領(lǐng)域服務(wù)训唱。
面紗七:兩關(guān)聯(lián)一循環(huán)
DDD 研發(fā)過(guò)程如下圖所示:
簡(jiǎn)單說(shuō)明一下:
- 業(yè)務(wù)人員(業(yè)務(wù)方)梳理需求,并向技術(shù)人員(技術(shù)方)澄清挚冤;
- 業(yè)務(wù)人員和技術(shù)人員一起通過(guò)事件風(fēng)暴捕獲行為需求况增,消化領(lǐng)域知識(shí),定義統(tǒng)一語(yǔ)言训挡,同時(shí)嘗試用統(tǒng)一語(yǔ)言來(lái)描述需求澳骤;
- 業(yè)務(wù)人員和技術(shù)人員一起完成戰(zhàn)略設(shè)計(jì)和戰(zhàn)術(shù)設(shè)計(jì),完善統(tǒng)一語(yǔ)言舍哄,更深刻的理解業(yè)務(wù)宴凉,同時(shí)確定分層架構(gòu)規(guī)范;
- 技術(shù)人員的代碼實(shí)現(xiàn)要受到領(lǐng)域模型和分層架構(gòu)規(guī)范的約束表悬,分離業(yè)務(wù)和技術(shù)的關(guān)注點(diǎn)弥锄,凸顯領(lǐng)域模型,同時(shí)基于面向模型的實(shí)現(xiàn)模式來(lái)準(zhǔn)確的表達(dá)領(lǐng)域模型,讓模型和代碼一一映射籽暇;
- 領(lǐng)域的業(yè)務(wù)需求“驅(qū)動(dòng)”出了代碼實(shí)現(xiàn)温治,同時(shí)代碼實(shí)現(xiàn)又“交付”了領(lǐng)域的業(yè)務(wù)需求,它們形成了一個(gè)正向反饋的循環(huán)戒悠,可以反復(fù)交替熬荆,迭代改進(jìn),使代碼的實(shí)現(xiàn)復(fù)雜度逐步逼近業(yè)務(wù)的本質(zhì)復(fù)雜度绸狐。
我們可以進(jìn)一步把 DDD 研發(fā)過(guò)程精簡(jiǎn)為兩關(guān)聯(lián)一循環(huán):
- 領(lǐng)域模型與統(tǒng)一語(yǔ)言關(guān)聯(lián)卤恳;
- 領(lǐng)域模型與代碼實(shí)現(xiàn)關(guān)聯(lián);
- 領(lǐng)域模型的演進(jìn)和精煉循環(huán)寒矿。
可見突琳,DDD 的核心是領(lǐng)域模型,修改模型就是修改代碼符相,修改代碼就是修改模型拆融。
我們都知道,軟件研發(fā)的核心難度在于處理隱藏在業(yè)務(wù)知識(shí)中的復(fù)雜度啊终,那么模型就是對(duì)這種復(fù)雜度的簡(jiǎn)化與精煉镜豹。所以從某種意義上說(shuō),Eric 倡導(dǎo)的 DDD 是一種模型驅(qū)動(dòng)的設(shè)計(jì)方法:通過(guò)領(lǐng)域模型來(lái)捕獲領(lǐng)域知識(shí)蓝牲,使用領(lǐng)域模型構(gòu)造更易維護(hù)的軟件趟脂。
起初,不要太在意獲得模型是否完美搞旭,是否在概念上足夠抽象散怖,是否使用了設(shè)計(jì)模式等。反而肄渗,我們更應(yīng)該關(guān)注該如何圍繞模型,建立有效的溝通與反饋機(jī)制咬最,形成兩關(guān)聯(lián)一循環(huán)過(guò)程翎嫡。理想的模型,需要是所有人都能懂的模型永乌,而不是滿是完美的模式和抽象的概念惑申。
團(tuán)隊(duì)通過(guò)迭代改進(jìn)法,不求一步到位翅雏,但求一次比一次好圈驼。通過(guò)兩關(guān)聯(lián)一循環(huán),技術(shù)人員與業(yè)務(wù)人員在不斷地交流與反饋中望几,逐步完成對(duì)模型的演進(jìn)和精煉绩脆。無(wú)論起點(diǎn)多么低,只要能夠持續(xù)改進(jìn),總有一天會(huì)有好結(jié)果的靴迫。而能夠支撐持續(xù)改進(jìn)基礎(chǔ)的惕味,則是實(shí)現(xiàn)方式與模型方式的一致。所以比起模型的好壞(總是會(huì)改好的)玉锌,關(guān)聯(lián)模型與代碼實(shí)現(xiàn)就變得更為重要了名挥。
由此我們可以更好地理解,DDD 并不是一種編碼技術(shù)主守,或是一種特定的編碼風(fēng)格禀倔,而是軟件研發(fā)的一種方法,貫穿軟件生命周期的全過(guò)程参淫,需要業(yè)務(wù)人員和技術(shù)人員的高效協(xié)同救湖。
經(jīng)常有 DDD 學(xué)員問(wèn)筆者,技術(shù)人員如何搞 DDD黄刚?筆者一般都會(huì)告訴他捎谨,要搞 DDD,必須有業(yè)務(wù)人員參與憔维,只有技術(shù)人員參與的 DDD 是偽 DDD涛救。業(yè)務(wù)人員和技術(shù)人員圍繞領(lǐng)域模型,建立有效的溝通與反饋機(jī)制业扒,形成兩關(guān)聯(lián)一循環(huán)過(guò)程检吆,這才是真正的 DDD,也是 DDD 能真正發(fā)揮威力的原因程储。
小結(jié)
本文嘗試揭開 DDD 的神秘面紗蹭沛,總共七層:
- DDD 發(fā)展了 OO,拋棄了將分析模型與設(shè)計(jì)模型分離的做法章鲤,而是尋找單個(gè)領(lǐng)域模型來(lái)滿足兩方面的要求摊灭;
- DDD 發(fā)展了敏捷,它顯式的把領(lǐng)域和設(shè)計(jì)放到了軟件研發(fā)的核心败徊,業(yè)務(wù)人員和技術(shù)人員被得到同樣的重視帚呼,他們通過(guò)協(xié)同來(lái)構(gòu)建并演進(jìn)領(lǐng)域模型;
- 統(tǒng)一語(yǔ)言是業(yè)務(wù)人員和技術(shù)人員使用事件風(fēng)暴實(shí)踐共同創(chuàng)建的一套語(yǔ)言皱蹦,必須在團(tuán)隊(duì)范圍內(nèi)達(dá)成一致煤杀,將技術(shù)人員的思考起點(diǎn)拉到了業(yè)務(wù)上,填平了業(yè)務(wù)人員和技術(shù)人員之間那道人為的鴻溝沪哺;
- 戰(zhàn)略設(shè)計(jì)屬于高層設(shè)計(jì)沈自,目標(biāo)是分離子域,拆分限界上下文辜妓,并確定上下文映射枯途,需要優(yōu)先完成忌怎;
- 戰(zhàn)術(shù)設(shè)計(jì)屬于低層設(shè)計(jì),目標(biāo)是得到一個(gè)簡(jiǎn)單自洽的領(lǐng)域模型柔袁,既易于理解呆躲,又可以低成本響應(yīng)需求的變化;
- 分層架構(gòu)將領(lǐng)域模型和其它支撐代碼進(jìn)行解耦捶索,可以凸顯領(lǐng)域模型插掂,有效地管理業(yè)務(wù)復(fù)雜度和技術(shù)復(fù)雜度;
- 兩關(guān)聯(lián)一循環(huán)是 DDD的內(nèi)核腥例,也是 DDD 能真正發(fā)揮威力的原因辅甥。
希望讀者通過(guò)本文可以快速俯瞰 DDD 的全貌和內(nèi)核,從而降低 DDD 的理解成本燎竖,提高 DDD 的實(shí)踐收益璃弄。