DDD學(xué)一步寫一篇--DDD開發(fā)實(shí)踐流程

DDD大家討論的比較多的一般都是DDD的思想和理論寨蹋,很少有文章討論具體是如何實(shí)施和落地迟隅,所以這也導(dǎo)致很多同學(xué)看完了Evans經(jīng)典巨著后對(duì)DDD還是不知道如何去實(shí)施贪庙。這篇文章我們討論下DDD的實(shí)施步驟牡昆,聊聊怎么一步步在項(xiàng)目中實(shí)施DDD砰诵。

在習(xí)慣了傳統(tǒng)的數(shù)據(jù)驅(qū)動(dòng)開發(fā)模式后雨席,View菩咨、Service、dao這種三層分層模式陡厘,開發(fā)者會(huì)很自然的寫出過程式代碼抽米,這種開發(fā)方式中的對(duì)象只是數(shù)據(jù)載體,而沒有行為糙置,是一種貧血對(duì)象模型云茸。以數(shù)據(jù)為中心,以數(shù)據(jù)庫ER圖為設(shè)計(jì)驅(qū)動(dòng)谤饭,分層架構(gòu)在這種開發(fā)模式下可以認(rèn)為是數(shù)據(jù)處理和實(shí)現(xiàn)的過程标捺。

數(shù)據(jù)驅(qū)動(dòng)模式業(yè)務(wù)邏輯都是寫在Service中的,定義的實(shí)體模型充其量只是個(gè)數(shù)據(jù)載體揉抵,沒有任何行為亡容。簡(jiǎn)單的業(yè)務(wù)系統(tǒng)采用這種貧血模型和過程化設(shè)計(jì)是沒有問題的,但在業(yè)務(wù)邏輯復(fù)雜了冤今,業(yè)務(wù)邏輯闺兢、狀態(tài)會(huì)散落到在大量方法中,原本的代碼意圖會(huì)漸漸不明確戏罢,我們將這種情況稱為由貧血癥引起的失憶癥屋谭。

所以才有了目前很多大牛推崇的DDD-領(lǐng)域驅(qū)動(dòng)開發(fā)模式,將實(shí)體模型的數(shù)據(jù)和行為封裝在一起龟糕,并與現(xiàn)實(shí)世界業(yè)務(wù)領(lǐng)域中的業(yè)務(wù)對(duì)象進(jìn)行映射戴而。將領(lǐng)域業(yè)務(wù)邏輯分散到領(lǐng)域?qū)ο笾小?/p>

在目前主流的微服務(wù)開發(fā)過程中,其實(shí)可以把DDD中的限界上下文看作一個(gè)微服務(wù)翩蘸,這個(gè)微服務(wù)對(duì)外提供的服務(wù)是高內(nèi)聚、低耦合的淮逊。微服務(wù)架構(gòu)強(qiáng)調(diào)的是從業(yè)務(wù)緯度去拆分系統(tǒng)催首,而DDD也是同樣看重業(yè)務(wù)領(lǐng)域扶踊。

DDD的核心訴求就是將業(yè)務(wù)架構(gòu)映射到系統(tǒng)架構(gòu)上,在響應(yīng)業(yè)務(wù)變化調(diào)整業(yè)務(wù)架構(gòu)時(shí)郎任,也隨之變化系統(tǒng)架構(gòu)秧耗。而微服務(wù)追求業(yè)務(wù)層面的復(fù)用,設(shè)計(jì)出來的系統(tǒng)架構(gòu)和業(yè)務(wù)一致舶治;在技術(shù)架構(gòu)上則系統(tǒng)模塊之間充分解耦分井,可以自由地選擇合適的技術(shù)架構(gòu),去中心化地治理技術(shù)和數(shù)據(jù)霉猛。

DDD設(shè)計(jì)流程

按照實(shí)現(xiàn)領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)一書中描述的DDD步驟主要有4步:

  1. 根據(jù)業(yè)務(wù)需求劃分出初步的領(lǐng)域和限界上下文尺锚,以及上下文之間的關(guān)系;
  2. 進(jìn)一步分析每個(gè)上下文內(nèi)部惜浅,識(shí)別出哪些是實(shí)體瘫辩,哪些是值對(duì)象;對(duì)實(shí)體坛悉、值對(duì)象進(jìn)行關(guān)聯(lián)和聚合伐厌,劃分出聚合的范疇和聚合根;
  3. 為聚合根設(shè)計(jì)倉(cāng)儲(chǔ)裸影,并思考實(shí)體或值對(duì)象的創(chuàng)建方式挣轨;
  4. 在工程中實(shí)踐領(lǐng)域模型,并在實(shí)踐中檢驗(yàn)?zāi)P偷暮侠硇孕桑雇颇P椭胁蛔愕牡胤讲⒅貥?gòu)卷扮。

1. 戰(zhàn)略建模

在拿到一個(gè)業(yè)務(wù)需求的時(shí)候,DDD首先需要進(jìn)行戰(zhàn)術(shù)建模界轩,戰(zhàn)術(shù)建模其實(shí)就是DDD的第一步画饥,根據(jù)具體業(yè)務(wù)區(qū)分子域,搞清楚各個(gè)限界上下文之間的關(guān)系浊猾。

限界上下文之間的映射關(guān)系主要有下面這幾種:

  • 合作關(guān)系(Partnership):兩個(gè)上下文緊密合作的關(guān)系抖甘,一榮俱榮,一損俱損葫慎。
  • 共享內(nèi)核(Shared Kernel):兩個(gè)上下文依賴部分共享的模型衔彻。
  • 客戶方-供應(yīng)方開發(fā)(Customer-Supplier Development):上下文之間有組織的上下游依賴。
  • 遵奉者(Conformist):下游上下文只能盲目依賴上游上下文偷办。
  • 防腐層(Anticorruption Layer):一個(gè)上下文通過一些適配和轉(zhuǎn)換與另一個(gè)上下文交互艰额。
  • 開放主機(jī)服務(wù)(Open Host Service):定義一種協(xié)議來讓其他上下文來對(duì)本上下文進(jìn)行訪問。
  • 發(fā)布語言(Published Language):通常與OHS一起使用椒涯,用于定義開放主機(jī)的協(xié)議柄沮。
  • 大泥球(Big Ball of Mud):混雜在一起的上下文關(guān)系,邊界不清晰。
  • 另謀他路(SeparateWay):兩個(gè)完全沒有任何聯(lián)系的上下文祖搓。

通過繪制全局的限界上下文的關(guān)系狱意,能夠梳理清楚業(yè)務(wù)需求中的各個(gè)領(lǐng)域、子領(lǐng)域之間的關(guān)系拯欧。

2. 戰(zhàn)術(shù)建南甓冢—細(xì)化上下文

戰(zhàn)術(shù)建模很重要的一步就是區(qū)分出整個(gè)限界上下文中的實(shí)體、值對(duì)象镐作、聚合藏姐,戰(zhàn)術(shù)建模其實(shí)對(duì)應(yīng)了Entity、ValueObject该贾、Aggregate羔杨。我們要提煉出業(yè)務(wù)中的精華,合理的抽象為這3個(gè)概念靶庙,并且這種抽象是需隨著領(lǐng)域里的概念變化而變化的问畅。這3者的結(jié)合運(yùn)用會(huì)讓我們的項(xiàng)目活起來,這是DDD的核心六荒。這里再把這3個(gè)概念重新梳理一下护姆。

? Entity(實(shí)體): 每個(gè)實(shí)體是唯一的,并且可以相當(dāng)長(zhǎng)的一段時(shí)間內(nèi)持續(xù)地變化掏击。我們可以對(duì)實(shí)體做多次修改卵皂,故一個(gè)實(shí)體對(duì)象可能和它先前的狀態(tài)大不相同。但是砚亭,由于它們擁有相同的身份標(biāo)識(shí)灯变,他們依然是同一個(gè)實(shí)體。例如一件商品在電商商品上下文中是一個(gè)實(shí)體捅膘,通過商品中臺(tái)唯一的商品id來標(biāo)示這個(gè)實(shí)體添祸。

? ValueObject(值對(duì)象):值對(duì)象用于度量和描述事物,當(dāng)你只關(guān)心某個(gè)對(duì)象的屬性時(shí)寻仗,該對(duì)象便可作為一個(gè)值對(duì)象刃泌。實(shí)體與值對(duì)象的區(qū)別在于唯一的身份標(biāo)識(shí)和可變性。當(dāng)一個(gè)對(duì)象用于描述一個(gè)事物署尤,但是又沒有唯一標(biāo)示耙替,那么它就是一個(gè)值對(duì)象。例如商品中的商品類別曹体,類別就沒有一個(gè)唯一標(biāo)示俗扇,通過圖書、服裝箕别、3C這些值就能明確表示這個(gè)商品類別铜幽。

不同上下文領(lǐng)域中的實(shí)體滞谢,在當(dāng)前上下文中需要建模為值對(duì)象,因?yàn)楫?dāng)前領(lǐng)域無法直接修改這些對(duì)象的內(nèi)部屬性除抛。

? Aggregate(聚合):聚合類是實(shí)體的升級(jí)爹凹,是由一組與生俱來就密切相關(guān)實(shí)體和值對(duì)象組合而成的,這整個(gè)組合的最上層實(shí)體就是聚合镶殷。聚合類建議設(shè)計(jì)成小聚合,可以只包含根實(shí)體微酬,而不需要包含其他實(shí)體绘趋,即使一定要包含,可以考慮將其設(shè)計(jì)成值對(duì)象颗管。通過唯一標(biāo)識(shí)來引用其他聚合或者實(shí)體陷遮,如果是外部上下文中的實(shí)體,引用其唯一標(biāo)識(shí)或?qū)⑿枰膶傩詷?gòu)造成值對(duì)象垦江。如果聚合類創(chuàng)建過于復(fù)雜可以將其的創(chuàng)建動(dòng)作封裝在工廠方法里帽馋。

聚合內(nèi)部多個(gè)組成對(duì)象的關(guān)系可以用來指導(dǎo)數(shù)據(jù)庫建表,但是肯定會(huì)存在一些問題比吭,例如最常見的問題绽族,一個(gè)聚合類中有一個(gè)List的值對(duì)象,那么在數(shù)據(jù)庫中建立1:N的關(guān)聯(lián)需要將值對(duì)象單獨(dú)建表衩藤,這時(shí)候值對(duì)象表的id就不要暴露到資源庫外部吧慢,讓外部不可見。

領(lǐng)域服務(wù):一些重要的領(lǐng)域行為可以定義為領(lǐng)域服務(wù)赏表,簡(jiǎn)單的原則可以認(rèn)為一些操作不適合放在實(shí)體或值對(duì)象检诗,那么就可以把這些領(lǐng)域的組合行為定義為領(lǐng)域服務(wù),這里的領(lǐng)域服務(wù)也有點(diǎn)類似于我們常用的3層架構(gòu)的service層瓢剿,但是不同的是逢慌,領(lǐng)域服務(wù)中是不包含實(shí)體類中對(duì)實(shí)體自己操作的行為,實(shí)體自操作的行為都是封裝在實(shí)體類內(nèi)部的间狂。一切領(lǐng)域邏輯的對(duì)外暴露都需要通過領(lǐng)域服務(wù)來完成攻泼。

3. 數(shù)據(jù)倉(cāng)儲(chǔ)設(shè)計(jì)

DDD的設(shè)計(jì)過程中很多同學(xué)對(duì)于數(shù)據(jù)倉(cāng)儲(chǔ)的設(shè)計(jì)存在疑問,不知道DO如何存儲(chǔ)到數(shù)據(jù)庫中前标,其實(shí)業(yè)務(wù)DO最后落表到數(shù)據(jù)庫中也沒有什么特殊的處理方式坠韩,主要還是將DO轉(zhuǎn)成PO(持久化對(duì)象)來進(jìn)行數(shù)據(jù)存儲(chǔ),PO的結(jié)構(gòu)是和存儲(chǔ)表結(jié)構(gòu)對(duì)應(yīng)的炼列,這樣就將DO的業(yè)務(wù)模型保存到了技術(shù)數(shù)據(jù)庫中只搁。下面介紹下整個(gè)DDD中數(shù)據(jù)轉(zhuǎn)換的流程:


DDD數(shù)據(jù)轉(zhuǎn)換流程2

首先領(lǐng)域的開放服務(wù)通過信息傳輸對(duì)象(DTO)來完成與外界的數(shù)據(jù)交互;在領(lǐng)域內(nèi)部俭尖,我們通過領(lǐng)域?qū)ο螅―O)作為領(lǐng)域內(nèi)部的數(shù)據(jù)和行為載體氢惋;在資源庫內(nèi)部洞翩,我們沿襲了原有的數(shù)據(jù)庫持久化對(duì)象(PO)進(jìn)行數(shù)據(jù)庫資源的交互。同時(shí)焰望,DTO與DO的轉(zhuǎn)換發(fā)生在領(lǐng)域服務(wù)內(nèi)骚亿,DO與PO的轉(zhuǎn)換發(fā)生在資源庫內(nèi)。

與以往的業(yè)務(wù)服務(wù)相比熊赖,當(dāng)前的編碼規(guī)范可能多造成了一次數(shù)據(jù)轉(zhuǎn)換来屠,但每種數(shù)據(jù)對(duì)象職責(zé)明確,數(shù)據(jù)流轉(zhuǎn)更加清晰震鹉。

//數(shù)據(jù)庫資源
import com.shrb.mobile.pay.card.repo.dao.CardDao;//數(shù)據(jù)庫訪問對(duì)象-銀行卡
import com.shrb.mobile.pay.card.repo.dao.po.CardPO;//數(shù)據(jù)庫持久化對(duì)象-銀行卡
import com.shrb.mobile.pay.card.repo.dao.po.CardTransferPO;//數(shù)據(jù)庫持久化對(duì)象-獎(jiǎng)池

import com.shrb.mobile.pay.card.repo.cache.CardCacheAccessObj;//分布式緩存訪問對(duì)象-銀行卡緩存訪問
import com.shrb.mobile.pay.card.repo.repository.CardRepository;//資源庫訪問對(duì)象-銀行卡資源庫

4. 工程實(shí)施

通過模塊將限界上下文進(jìn)行區(qū)分俱笛,可以通過com.公司名.歸屬團(tuán)隊(duì).業(yè)務(wù).上下文.* 這樣的包命名結(jié)構(gòu)進(jìn)行命名,這樣可以將一個(gè)上下文限定在一個(gè)包的內(nèi)部传趾。

import com.shrb.mobile.pay.card.*;//支付卡上下文
import com.shrb.mobile.pay.riskcontrol.*;//支付交易風(fēng)控上下文
import com.shrb.mobile.pay.route.*;//支付路由上下文
import com.shrb.mobile.pay.thirdparty.*;//外部三方支付上下文

對(duì)于模塊內(nèi)的組織結(jié)構(gòu)迎膜,一般情況下我們是按照領(lǐng)域?qū)ο蟆㈩I(lǐng)域服務(wù)浆兰、領(lǐng)域資源庫磕仅、防腐層等組織方式進(jìn)行定義,這樣也可以和DDD中各個(gè)概念對(duì)應(yīng)起來簸呈。

import com.shrb.mobile.pay.card.domain.valobj.*;//領(lǐng)域?qū)ο?值對(duì)象
import com.shrb.mobile.pay.card.domain.entity.*;//領(lǐng)域?qū)ο?實(shí)體
import com.shrb.mobile.pay.card.domain.aggregate.*;//領(lǐng)域?qū)ο?聚合根
import com.shrb.mobile.pay.card.service.*;//領(lǐng)域服務(wù)
import com.shrb.mobile.pay.card.repo.*;//領(lǐng)域資源庫
import com.shrb.mobile.pay.card.acl.*;//領(lǐng)域防腐層

5. 防腐層

上面介紹了DDD在具體實(shí)施過程中的4個(gè)主要步驟榕订,但是在有些情況下還需要引入防腐層,有以下幾種情況會(huì)考慮引入防腐層:

  • 需要將外部上下文中的模型翻譯成本上下文理解的模型蝶棋。
  • 不同上下文之間的團(tuán)隊(duì)協(xié)作關(guān)系卸亮,如果是供奉者關(guān)系,建議引入防腐層玩裙,避免外部上下文變化對(duì)本上下文的侵蝕兼贸。
  • 該訪問本上下文使用廣泛,為了避免改動(dòng)影響范圍過大吃溅。

將領(lǐng)域行為封裝到領(lǐng)域?qū)ο笾腥艿瑢①Y源管理行為封裝到資源庫中,將外部上下文的交互行為封裝到防腐層中决侈。能夠發(fā)現(xiàn)領(lǐng)域服務(wù)本身所承載的職責(zé)也就更加清晰了螺垢,即就是通過串聯(lián)領(lǐng)域?qū)ο蟆①Y源庫和防腐層等一系列領(lǐng)域內(nèi)的對(duì)象的行為赖歌,對(duì)其他上下文提供交互的接口枉圃。

其實(shí)我的理解防腐層就是一個(gè)不同限界上下文之間的Adapter,主要做的工作就是數(shù)據(jù)轉(zhuǎn)換庐冯,將其他領(lǐng)域的實(shí)體轉(zhuǎn)換成當(dāng)前限界上下文可以處理的實(shí)體孽亲,在不同上下文中做了一層隔離,這樣當(dāng)其他限界上下文業(yè)務(wù)實(shí)體有改動(dòng)的時(shí)候展父,可以將對(duì)本限界上下文的影響降到最小返劲。

總結(jié)

這是寫的關(guān)于DDD的第二篇文章玲昧,這端時(shí)間主要看了領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)和領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)實(shí)現(xiàn)兩本書,兩本書總體還是偏抽象理論篮绿,但是一旦理解了核心理念孵延,再結(jié)合目前自己非常熟悉的業(yè)務(wù)領(lǐng)域,那么對(duì)DDD會(huì)有更深入的理解亲配。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末尘应,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子吼虎,更是在濱河造成了極大的恐慌菩收,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,277評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件鲸睛,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡坡贺,警方通過查閱死者的電腦和手機(jī)官辈,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來遍坟,“玉大人拳亿,你說我怎么就攤上這事≡赴椋” “怎么了肺魁?”我有些...
    開封第一講書人閱讀 163,624評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)隔节。 經(jīng)常有香客問我鹅经,道長(zhǎng),這世上最難降的妖魔是什么怎诫? 我笑而不...
    開封第一講書人閱讀 58,356評(píng)論 1 293
  • 正文 為了忘掉前任瘾晃,我火速辦了婚禮,結(jié)果婚禮上幻妓,老公的妹妹穿的比我還像新娘蹦误。我一直安慰自己,他們只是感情好肉津,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評(píng)論 6 392
  • 文/花漫 我一把揭開白布强胰。 她就那樣靜靜地躺著,像睡著了一般妹沙。 火紅的嫁衣襯著肌膚如雪偶洋。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,292評(píng)論 1 301
  • 那天初烘,我揣著相機(jī)與錄音涡真,去河邊找鬼分俯。 笑死,一個(gè)胖子當(dāng)著我的面吹牛哆料,可吹牛的內(nèi)容都是我干的缸剪。 我是一名探鬼主播,決...
    沈念sama閱讀 40,135評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼东亦,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼杏节!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起典阵,我...
    開封第一講書人閱讀 38,992評(píng)論 0 275
  • 序言:老撾萬榮一對(duì)情侶失蹤奋渔,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后壮啊,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體嫉鲸,經(jīng)...
    沈念sama閱讀 45,429評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評(píng)論 3 334
  • 正文 我和宋清朗相戀三年歹啼,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了玄渗。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,785評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡狸眼,死狀恐怖藤树,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情拓萌,我是刑警寧澤岁钓,帶...
    沈念sama閱讀 35,492評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站微王,受9級(jí)特大地震影響屡限,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜炕倘,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評(píng)論 3 328
  • 文/蒙蒙 一囚霸、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧激才,春花似錦拓型、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至东帅,卻和暖如春压固,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背靠闭。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工帐我, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留坎炼,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,891評(píng)論 2 370
  • 正文 我出身青樓拦键,卻偏偏與公主長(zhǎng)得像谣光,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子芬为,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容