10.領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)(譯)

原文:https://herbertograca.com/2017/09/07/domain-driven-design/

這篇文章是軟件架構(gòu)編年史()的一部分,這部編年史由一系列關(guān)于軟件架構(gòu)的文章組成勋眯。在這一系列文章中著拭,我將寫下我對(duì)軟件架構(gòu)的學(xué)習(xí)和思考额获,以及我是如何運(yùn)用這些知識(shí)的装畅。如果你閱讀了這個(gè)系列中之前的文章,本篇文章的的內(nèi)容將更有意義嗽元。

Eric Evans 于 2003 年出版了精采絕倫的《領(lǐng)域驅(qū)動(dòng)設(shè)計(jì):軟件核心復(fù)雜性應(yīng)對(duì)之道》敛纲,在書中他創(chuàng)造了領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)方法。Eric Evans 的這本著作十分重要剂癌,現(xiàn)今許多我們認(rèn)為理所當(dāng)然的軟件開發(fā)概念都是在本書中被正式提出的淤翔。

我不可能在一篇博客中全面地回顧 DDD。和 DDD 相關(guān)的重要概念實(shí)在是太多了佩谷。幸好旁壮,這篇文章志不在此。而我要做的就是列出一些 DDD 概念谐檀,我認(rèn)為這些概念對(duì)我喜歡的代碼組織方式和我對(duì)架構(gòu)的看法而言更有意義:系統(tǒng)范圍內(nèi)構(gòu)成特性開發(fā)基礎(chǔ)的那些概念抡谐。

在這篇文章里,我將著重探討:

  • 統(tǒng)一語言
  • 分層
  • 限界上下文
  • 防腐層
  • 共享內(nèi)核
  • 通用子域

統(tǒng)一語言

在軟件開發(fā)中桐猬,圍繞著代碼的理解始終有一些問題麦撵,代碼是什么,它們干了什么溃肪,它們?nèi)绾巫龅降拿馕福鼈優(yōu)槭裁匆@么做...如果代碼中使用的術(shù)語和領(lǐng)域?qū)<沂褂玫男g(shù)語不一樣的話就更復(fù)雜了,例如惫撰,領(lǐng)域?qū)<艺f的是老用戶(elder user)而代碼說的卻是管理者(supervisor)羔沙,在討論應(yīng)用時(shí)這可能帶來很多的困擾。但是润绎,絕大多數(shù)的混淆都可以通過類和方法的正確命名來解決撬碟,正確的命名能讓它們表達(dá)對(duì)象在領(lǐng)域上下文中是什么以及方法在領(lǐng)域上下文中干了什么诞挨。

統(tǒng)一語言的主要思想是讓應(yīng)用能和業(yè)務(wù)相匹配。這是通過在業(yè)務(wù)與代碼中的技術(shù)之間采用共同的語言達(dá)成呢蛤。這門語言起源于公司的業(yè)務(wù)側(cè)-他們擁有需要實(shí)現(xiàn)的概念惶傻,語言中的術(shù)語由他們和公司的技術(shù)側(cè)通過協(xié)商來定義(意味著業(yè)務(wù)側(cè)也不能總是選到最好的命名),目標(biāo)是創(chuàng)造可以被業(yè)務(wù)其障、技術(shù)和代碼自身無歧義使用的共同術(shù)語银室,即統(tǒng)一語言。代碼励翼、類蜈敢、方法、屬性和模塊的命名必須和統(tǒng)一語言相匹配汽抚。必要的時(shí)候需要對(duì)代碼進(jìn)行重構(gòu)抓狭!

分層

之前的文章中我已經(jīng)談到過分層,但我覺得這里關(guān)鍵的是記住通過 DDD 識(shí)別的層次:

  • 用戶界面
    負(fù)責(zé)繪制用戶用來和應(yīng)用交互的屏幕界面并將用戶的輸入翻譯成應(yīng)用的命令造烁。值得注意的是“用戶”可以是人類也可以是連接我們 API 的其他應(yīng)用否过,它們和EBI架構(gòu)中的邊界對(duì)象完全對(duì)應(yīng)。

  • 應(yīng)用層
    協(xié)調(diào)領(lǐng)域?qū)ο笸瓿捎脩粢蟮娜蝿?wù):用例惭蟋。它不包含業(yè)務(wù)邏輯苗桂。應(yīng)用層和EBI架構(gòu)中的交互器相對(duì)應(yīng),只有一點(diǎn)不同告组,交互器是和界面或?qū)嶓w無關(guān)的任意對(duì)象煤伟,而這里應(yīng)用層只包含和用例相關(guān)的對(duì)象。應(yīng)用服務(wù)屬于這一層木缝,它們是用例對(duì)資源庫便锨、領(lǐng)域模型、實(shí)體氨肌、值對(duì)象或是任何其它領(lǐng)域?qū)ο筮M(jìn)行編配的容器鸿秆。

  • 領(lǐng)域?qū)?br> 這個(gè)層次包含了所有的業(yè)務(wù)邏輯,如領(lǐng)域服務(wù)怎囚、實(shí)體卿叽、事件和其他包含業(yè)務(wù)邏輯的任意對(duì)象類型。顯然它和 EBI 架構(gòu)中的實(shí)體對(duì)象類型對(duì)應(yīng)恳守。這是系統(tǒng)的心臟考婴。領(lǐng)域服務(wù)擺包含的領(lǐng)域邏輯不太適合放到某個(gè)實(shí)體中,通常是為了完成某個(gè)領(lǐng)域操作而對(duì)多個(gè)實(shí)體進(jìn)行的編配催烘。

  • 基礎(chǔ)設(shè)施
    支持上述三個(gè)層次的技術(shù)能力沥阱,例如,持久化或者消息機(jī)制伊群。

限界上下文

在企業(yè)應(yīng)用中考杉,模型的規(guī)模和在代碼倉庫上工作的團(tuán)隊(duì)規(guī)模都增長(zhǎng)得很快策精。這會(huì)給我們帶來兩個(gè)問題:

  1. 開發(fā)者工作的代碼倉庫越大,認(rèn)知超載就越嚴(yán)重崇棠,代碼就越難理解咽袜,這會(huì)導(dǎo)致 BUG 的產(chǎn)生和錯(cuò)誤的判斷;
  2. 在同一個(gè)代碼倉庫上工作的開發(fā)者越多枕稀,就越難協(xié)作并達(dá)成共同的應(yīng)用領(lǐng)域和技術(shù)愿景询刹。

換句話說,我們面臨的問題太大了萎坷。

通常的解決方法就是把大問題切分成較小的問題凹联,“限界上下文”就是這樣干的。

一般來說哆档,兩個(gè)子系統(tǒng)一定服務(wù)于迥然不同的用戶群體蔽挠。——Eric Evans 2014, Domain-Driven Design Reference

限界上下文定義了模型中隔離出來的部分可以應(yīng)用的上下文瓜浸。這種隔離可以通過解耦技術(shù)邏輯象泵,分割代碼倉庫,分割數(shù)據(jù)庫 Schema 來達(dá)成斟叼,在團(tuán)隊(duì)組織方面也是一樣。和往常一樣春寿,限界上下文將拆分到何種程度取決于實(shí)際情況:我們的需求和可能性朗涩。

有趣的是,這不是一個(gè)全新的概念绑改。早在 1992 年谢床,Ivar Jacobson 在他的中就有子系統(tǒng)的描述,比 Eric Evans 早了十一年厘线!

那時(shí)他就提出了一些關(guān)于這個(gè)主題的具體想法:

  • 系統(tǒng)由若干子系統(tǒng)組成识腿,而它們各自又有各自的子系統(tǒng)。這個(gè)層級(jí)結(jié)構(gòu)的最底層就是分析對(duì)象造壮。于是子系統(tǒng)就成為了進(jìn)一步開發(fā)和維護(hù)系統(tǒng)的結(jié)構(gòu)方式渡讼。
  • 子系統(tǒng)的任務(wù)就是把對(duì)象組合成包,達(dá)到降低復(fù)雜度的目的耳璧。
  • 和功能的特定部分相關(guān)的全部對(duì)象都將被放在同一個(gè)子系統(tǒng)中成箫。
  • 目標(biāo)是子系統(tǒng)內(nèi)的強(qiáng)功能性耦合和子系統(tǒng)間的弱耦合(現(xiàn)在被稱為高內(nèi)聚低耦合)
  • [一個(gè)子系統(tǒng)]最好應(yīng)該只和一個(gè)角色耦合,因?yàn)樽兓ǔS梢粋€(gè)角色引發(fā)旨枯。
  • [...]首先把控制對(duì)象放入子系統(tǒng)蹬昌,然后將強(qiáng)耦合的實(shí)體對(duì)象和界面對(duì)象放到同一個(gè)子系統(tǒng)中
  • 擁有強(qiáng)相關(guān)功能耦合的所有對(duì)象都將被放入同一個(gè)子系統(tǒng)之中[...]
    • 一個(gè)對(duì)象中的變化會(huì)導(dǎo)致其它對(duì)象中的變化嗎?(現(xiàn)在被稱作共同封閉原則——一起變化的類應(yīng)該放在同一個(gè)包中——由 Robert C. Martin 在他 1996 年的論文“Granularity
      ”中發(fā)布攀隔,比 Ivar Jacobson 的書晚了四年)
    • 它們是和同一個(gè)角色通信嗎皂贩?
    • 這兩個(gè)對(duì)象都依賴第三個(gè)對(duì)象嗎栖榨?例如同一個(gè)界面對(duì)象或?qū)嶓w對(duì)象?
    • 這個(gè)對(duì)象會(huì)執(zhí)行多個(gè)其它對(duì)象上的操作嗎明刷?(現(xiàn)在被稱作共同重用原則——一起被使用的類應(yīng)該放在同一個(gè)包中——由 Robert C. Martin 在他 1996 年的論文“Granularity
      ”中發(fā)布婴栽,比 Ivar Jacobson 的書晚了四年)
  • 子系統(tǒng)劃分的另一個(gè)標(biāo)準(zhǔn)是不同子系統(tǒng)之間的通信應(yīng)該盡可能少(低耦合)
  • 對(duì)大型項(xiàng)目來做,還有其它一些子系統(tǒng)劃分的標(biāo)準(zhǔn)遮精,例如:
    • 不同的開發(fā)小組擁有不同的能力或者資源冯事,針對(duì)性地分配開發(fā)任務(wù)也許是值得的(這些小組還可能分布在不同的地點(diǎn))
    • 在分布式環(huán)境中,每個(gè)邏輯節(jié)點(diǎn)需要的可能就是一個(gè)子系統(tǒng)(SOA格带、Web 服務(wù)以及微服務(wù))邪媳。
    • 如果現(xiàn)存的產(chǎn)品可以在系統(tǒng)中使用,它可以被認(rèn)為是一個(gè)子系統(tǒng)(我們的系統(tǒng)所依賴的庫檬洞,例如ORM)

防腐層

防腐層基本就是兩個(gè)系統(tǒng)之間的中間件狸膏。它用來隔離兩個(gè)子系統(tǒng),讓它們都依賴防腐層而不是直接互相依賴添怔。這樣湾戳,如果我們重構(gòu)或者完全替換掉其中一個(gè)子系統(tǒng)時(shí),只需要更新防腐層广料,而不需要?jiǎng)悠渌淖酉到y(tǒng)砾脑。

在將一個(gè)新系統(tǒng)和遺留系統(tǒng)進(jìn)行集成時(shí)防腐層特別有用。為了不讓遺留的結(jié)構(gòu)限制我們?cè)O(shè)計(jì)新系統(tǒng)的想像力艾杏,我們會(huì)創(chuàng)建一個(gè)防腐層韧衣,將遺留子系統(tǒng)的 API 按照新的子系統(tǒng)的需要進(jìn)行適配。

它有三個(gè)主要關(guān)注點(diǎn):

  1. 按照客戶端子系統(tǒng)的需要對(duì)其它子系統(tǒng) API 進(jìn)行適配购桑;
  2. 對(duì)系統(tǒng)間傳遞的數(shù)據(jù)和命令進(jìn)行轉(zhuǎn)換畅铭;
  3. 根據(jù)需要建立單向或多向的通信。

當(dāng)我們無法控制全部子系統(tǒng)或某個(gè)子系統(tǒng)時(shí)勃蜘,使用這項(xiàng)技術(shù)的理由更加充分硕噩。但在我們能控制所有涉及的子系統(tǒng)時(shí),這項(xiàng)技術(shù)也有意義缭贡,盡管這些子系統(tǒng)設(shè)計(jì)良好只是擁有大相徑庭的模型炉擅,但是我們想要阻止一個(gè)模型對(duì)另一個(gè)模型的侵蝕(為了滿足一個(gè)子系統(tǒng)的需要而修改另一個(gè)子系統(tǒng))。

共享內(nèi)核

在某些情況下匀归,我們除了渴望完全隔離和解耦的組件之外坑资,在多個(gè)組件之間共享一些領(lǐng)域代碼也很有意義。

這會(huì)讓組件之間保持解耦穆端,盡管他們會(huì)和同一份代碼——共享內(nèi)核——耦合在一起袱贮。

例如,由一個(gè)組件觸發(fā)并由另外一個(gè)或多個(gè)組件監(jiān)聽的事件就是這樣的例子。但服務(wù)接口和事件實(shí)體也可能是這樣攒巍。

不過嗽仪,我們應(yīng)該限制共享內(nèi)核的大小,對(duì)它進(jìn)行修改時(shí)要小心翼翼柒莉,才不會(huì)毫不知情地破壞使用它的代碼闻坚。共享內(nèi)核中的代碼修改必須經(jīng)過其它使用它的團(tuán)隊(duì)的同意,這一點(diǎn)非常重要兢孝。

通用子域

子域是領(lǐng)域中非常獨(dú)立的一部分窿凤。通用子域不是特定于某個(gè)應(yīng)用的子域,它可以在任何類似的應(yīng)用中使用跨蟹。

例如雳殊,如果我們的應(yīng)用中有一部分是關(guān)于財(cái)務(wù)的,也許我們可以在應(yīng)用中使用現(xiàn)有的財(cái)務(wù)相關(guān)的庫窗轩。但是夯秃,無論以哪種方式實(shí)現(xiàn),哪怕我們沒有現(xiàn)成的庫可用而要自己構(gòu)建痢艺,如果這部分是通用子域仓洼,那么它就不是我們的核心業(yè)務(wù),它應(yīng)該被當(dāng)作必要的而不是決定性的因素堤舒。它不是我們應(yīng)用中最重要的部分色建,所以不是我們最好的專家重點(diǎn)關(guān)注的地方,毫無疑問它甚至不應(yīng)該在主要的源代碼之中出現(xiàn)舌缤,它可能是通過依賴管理工具安裝的镀岛。

總結(jié)

再一次聲明,這里我選擇探討多是關(guān)于單一職責(zé)友驮、低耦合、高內(nèi)聚驾锰、邏輯隔離的 DDD 概念卸留,這樣我們的應(yīng)用才能更一致、更簡(jiǎn)單椭豫、更快地響應(yīng)變化并適應(yīng)業(yè)務(wù)的需要耻瑟。

引用來源

1992 – Ivar Jacobson – Object-Oriented Software Engineering: A use case driven approach
1996 – Robert C. Martin – Granularity
2003 – Eric Evans – Domain-Driven Design: Tackling Complexity in the Heart of Software
2014 – Eric Evans – Domain-Driven Design Reference

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市赏酥,隨后出現(xiàn)的幾起案子喳整,更是在濱河造成了極大的恐慌,老刑警劉巖裸扶,帶你破解...
    沈念sama閱讀 206,482評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件框都,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡呵晨,警方通過查閱死者的電腦和手機(jī)魏保,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門熬尺,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人谓罗,你說我怎么就攤上這事粱哼。” “怎么了檩咱?”我有些...
    開封第一講書人閱讀 152,762評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵揭措,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我刻蚯,道長(zhǎng)绊含,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,273評(píng)論 1 279
  • 正文 為了忘掉前任芦倒,我火速辦了婚禮艺挪,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘兵扬。我一直安慰自己麻裳,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,289評(píng)論 5 373
  • 文/花漫 我一把揭開白布器钟。 她就那樣靜靜地躺著津坑,像睡著了一般。 火紅的嫁衣襯著肌膚如雪傲霸。 梳的紋絲不亂的頭發(fā)上疆瑰,一...
    開封第一講書人閱讀 49,046評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音昙啄,去河邊找鬼穆役。 笑死,一個(gè)胖子當(dāng)著我的面吹牛梳凛,可吹牛的內(nèi)容都是我干的耿币。 我是一名探鬼主播,決...
    沈念sama閱讀 38,351評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼韧拒,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼淹接!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起叛溢,我...
    開封第一講書人閱讀 36,988評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤塑悼,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后楷掉,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體厢蒜,經(jīng)...
    沈念sama閱讀 43,476評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,948評(píng)論 2 324
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了郭怪。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片支示。...
    茶點(diǎn)故事閱讀 38,064評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖鄙才,靈堂內(nèi)的尸體忽然破棺而出颂鸿,到底是詐尸還是另有隱情,我是刑警寧澤攒庵,帶...
    沈念sama閱讀 33,712評(píng)論 4 323
  • 正文 年R本政府宣布嘴纺,位于F島的核電站,受9級(jí)特大地震影響浓冒,放射性物質(zhì)發(fā)生泄漏栽渴。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,261評(píng)論 3 307
  • 文/蒙蒙 一稳懒、第九天 我趴在偏房一處隱蔽的房頂上張望闲擦。 院中可真熱鬧,春花似錦场梆、人聲如沸墅冷。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽寞忿。三九已至,卻和暖如春顶岸,著一層夾襖步出監(jiān)牢的瞬間腔彰,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評(píng)論 1 262
  • 我被黑心中介騙來泰國打工辖佣, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留霹抛,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,511評(píng)論 2 354
  • 正文 我出身青樓卷谈,卻偏偏與公主長(zhǎng)得像上炎,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子雏搂,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,802評(píng)論 2 345

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