結(jié)論先行:
在 DDD 中,通用語言是以限界上下文為邊界的庆杜。如果一個(gè)產(chǎn)品或者項(xiàng)目有多個(gè)限界上下文,我們就需要為每個(gè)限界上下文定義通用語言。
限界上下文提供了一個(gè)語義邊界垦垂,來保持通用語言和領(lǐng)域概念的一一對(duì)應(yīng)關(guān)系。
這個(gè)約束解決了現(xiàn)實(shí)世界中同樣的名詞在不同場(chǎng)景牙瓢、時(shí)機(jī)下對(duì)應(yīng)不同的業(yè)務(wù)概念所帶來的歧義問題劫拗,幫助團(tuán)隊(duì)在使用通用語言交流的時(shí)候可以無歧義溝通。
初嘗“通用語言”
最初我對(duì)于如何構(gòu)建通用語言的認(rèn)識(shí)矾克,來自于《領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)》第一章中的案例页慷。這個(gè)案例生動(dòng)的展示了開發(fā)人員如何在和領(lǐng)域?qū)<业臏贤ㄟ^程中,建立了雙方理解一致的通用語言,并且使用這個(gè)語言來進(jìn)行雙方的溝通酒繁∽艺茫基于那個(gè)案例,我當(dāng)時(shí)對(duì)構(gòu)建通用語言的理解就是要:
- 技術(shù)人員使用業(yè)務(wù)人員的用語作為開發(fā)詞匯欲逃;
- 劃分好聚合,將這些詞匯關(guān)聯(lián)到聚合上;
- 技術(shù)人員要將這些詞匯映射到代碼實(shí)現(xiàn)中饼暑;
- 這些詞匯會(huì)隨著項(xiàng)目的發(fā)展一點(diǎn)點(diǎn)擴(kuò)展;
帶著這份理解稳析,我在曾經(jīng)負(fù)責(zé)過的小型項(xiàng)目上做了一些實(shí)踐,效果都很不錯(cuò)弓叛。在很長一段時(shí)間彰居,團(tuán)隊(duì)的開發(fā)人員體會(huì)到了在和業(yè)務(wù)人員交流時(shí)候心有靈犀、會(huì)心一笑的快感撰筷;也很少聽到“這個(gè)東西不是我要的”這類批評(píng)了陈惰。
“通用語言”遇到同名詞匯時(shí)就變得不清不楚了
然而,當(dāng)我來到ThoughtWorks參與到一些幾十號(hào)人的項(xiàng)目時(shí)毕籽,我發(fā)現(xiàn)根據(jù)這個(gè)原則構(gòu)建起來的通用語言抬闯,在遇到同名多義的詞匯時(shí),就無法保證團(tuán)隊(duì)內(nèi)部的溝通是無歧義的关筒。而這種歧義又會(huì)導(dǎo)致團(tuán)隊(duì)成員說著同樣的話想著不同的事情的情況出現(xiàn)溶握,例如:
- 同名的業(yè)務(wù)詞匯與實(shí)際業(yè)務(wù)關(guān)系不清:“為什么不能給銷售訂單增加一個(gè)是否投訴的字段,界面上都是顯示在銷售訂單上的”——銷售訂單到底是個(gè)什么東西蒸播,能干什么不能干什么是怎么確定的睡榆?
- 同名的業(yè)務(wù)詞匯與不同的業(yè)務(wù)詞匯關(guān)聯(lián):“我在銷售訂單付款后改變了買家信息,為什么我看銷售訂單的預(yù)定里的買家也發(fā)生了改變”——這里說的買家信息有幾個(gè)袍榆?
- 同名的業(yè)務(wù)詞匯之間的關(guān)系不清楚:“為什么我變更了profile 上的買家地址胀屿,銷售訂單上的買家地址就跟著改變了” ——這里說訂單上的買家地址和profile 上的買家地址是一個(gè)什么關(guān)系?
通過添加約束消除歧義
下圖是 DDD 概念的一個(gè)元模型圖包雀。從圖的左下角宿崭,我們可以看到在構(gòu)建通用語言時(shí),還有兩個(gè)額外的約束條件:子域和限界上下文才写。
在 DDD 中劳曹,軟件的核心是其為客戶解決領(lǐng)域相關(guān)的問題的能力
這里的領(lǐng)域,就是指軟件系統(tǒng)要解決的實(shí)際問題相關(guān)的東西的集合琅摩。
例如:為一個(gè)電子商務(wù)公司開發(fā)一個(gè)電商系統(tǒng)铁孵,我們就需要圍繞這個(gè)盈利模式的運(yùn)營方式、業(yè)務(wù)規(guī)則房资,比如如何進(jìn)貨蜕劝,如何促銷,如何物流等等了解這個(gè)電子商務(wù)公司的盈利模式,所有和業(yè)務(wù)相關(guān)的東西都屬于領(lǐng)域岖沛。
領(lǐng)域分為問題域和解決方案域兩部分暑始。
為了分解問題域的復(fù)雜度,問題域又會(huì)被拆解為多個(gè)子域婴削,每個(gè)子域都要明確待解決的業(yè)務(wù)問題和業(yè)務(wù)流程廊镜,以及通過解決業(yè)務(wù)問題為企業(yè)帶來了什么樣的業(yè)務(wù)價(jià)值(這個(gè)是因,業(yè)務(wù)流程和要解決的業(yè)務(wù)問題是果)唉俗。
在清晰的定義子域后嗤朴,我們就可以建立通用語言來提取該子域的領(lǐng)域知識(shí),并基于通用語言為解決問題建立領(lǐng)域模型虫溜。
一個(gè)領(lǐng)域模型會(huì)存在于一個(gè)限界上下文中雹姊。限界上下文在 DDD 中用來定義模型的適用范圍、模型的用途衡楞、以及在何處保持一致吱雏,限界上下文會(huì)讓團(tuán)隊(duì)明確模型的職責(zé)邊界是什么。同時(shí)瘾境,通用語言被限定在限界上下文中歧杏;限界上下文提供了一個(gè)語義邊界,在每個(gè)限界上下文內(nèi)通用語言的每個(gè)詞匯必須和領(lǐng)域概念一一對(duì)應(yīng)迷守。
理想條件下得滤,子域和限界上下文是一一對(duì)應(yīng)。但是子域劃分的粒度盒犹,遺留系統(tǒng)的現(xiàn)狀懂更,語言的歧義,團(tuán)隊(duì)結(jié)構(gòu)等子域和限界上下文對(duì)應(yīng)可能是1:N 或者 N:N 的急膀。
通過限界上下文間的映射沮协,上下文中的多個(gè)模型會(huì)協(xié)作以滿足系統(tǒng)需求。我們也可以了解在不同上下文中的同名詞匯是否存在關(guān)系卓嫂,存在什么樣的關(guān)系慷暂。
對(duì)通用語言而言,子域解釋了通用語言和現(xiàn)實(shí)世界業(yè)務(wù)活動(dòng)的關(guān)系晨雳;限界上下文提供了一個(gè)語義邊界行瑞,來保持通用語言和領(lǐng)域概念的一一對(duì)應(yīng)關(guān)系;上下文映射則提供了不同限界上下中的通用語言的轉(zhuǎn)換關(guān)系餐禁。
來解決下前文的問題
前文所述的訂單及訂單的相關(guān)概念存在著歧義血久,我們來看下通過子域、限界上下文和上下文映射是怎么消除這些歧義的:
因?yàn)橥臉I(yè)務(wù)詞匯與實(shí)際業(yè)務(wù)關(guān)系不清導(dǎo)致的疑惑
“為什么不能在銷售訂單中增加一個(gè)是否投訴的字段帮非,界面上都是顯示在銷售訂單上的”
假設(shè)氧吐,這里所說的銷售訂單存在于銷售子域下讹蘑,那么這個(gè)訂單應(yīng)該解決的是銷售過程中的問題。訂單的生命周期以銷售開始到銷售終止筑舅。一般而言投訴屬于售后環(huán)節(jié)座慰,在銷售訂單上聲明是否投訴字段,意味著銷售訂單的職能突破了銷售子域翠拣。UI 上的銷售訂單展示了聚合的信息版仔,和同名的領(lǐng)域模型不一定保持一致。
因?yàn)橥臉I(yè)務(wù)詞匯與不同的業(yè)務(wù)詞匯關(guān)聯(lián)導(dǎo)致的疑惑
“我在訂單付款后改變了買家信息误墓,為什么我看訂單的預(yù)定里的買家也發(fā)生了改變”
在訂單上有兩種買家信息蛮粮,可以通過在不同的上下文中隔離來區(qū)別這兩個(gè)擁有相同含義但卻是不同詞匯的詞匯。在銷售子域中建立兩個(gè)上下文优烧,分別為預(yù)定有界上下文和購買上下文蝉揍,把訂單領(lǐng)域模型拆分到這兩個(gè)上下文中链峭。在不同的上下文中畦娄,訂單都有自己的買家信息,就解決了“在訂單付款后改變了買家信息弊仪,為什么我看訂單的預(yù)定里的買家也發(fā)生了改變”這個(gè)問題熙卡。
因?yàn)橥臉I(yè)務(wù)詞匯之間的關(guān)系不清楚導(dǎo)致的疑惑
“為什么我變更了profile 上的買家地址,訂單上的買家地址就跟著改變了”
訂單存在于購買上下文励饵,profile 存在于身份信息上下文中驳癌,購買上下文和身份信息上下文存在映射關(guān)系,在訂單創(chuàng)建時(shí)候從身份信息上下文復(fù)制買家地址役听,在訂單中單獨(dú)保存颓鲜。這樣就解決了“為什么我變更了profile 上的買家地址,訂單上的買家地址就跟著改變了” 的問題典予。
引用:
- 《領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)》
- 《實(shí)現(xiàn)領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)》
- 當(dāng)Subdomain遇見Bounded Context
- DDD的終極大招——By Experience
- 《領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)學(xué)習(xí):領(lǐng)域甜滨、子域、限界上下文》
文/ThoughtWorks王巖
更多精彩洞見瘤袖,請(qǐng)關(guān)注微信公眾號(hào):ThoughtWorks洞見