當(dāng)今的企業(yè)應(yīng)用程序無疑是復(fù)雜的紊馏,并且依靠某些專門技術(shù)(持久性牺弄,AJAX姻几,Web服務(wù)等)來完成其工作。作為開發(fā)人員势告,我們傾向于專注于這些技術(shù)細(xì)節(jié)是可以理解的蛇捌。但是事實(shí)是,不能解決業(yè)務(wù)需求的系統(tǒng)對(duì)任何人都沒有用咱台,無論它的外觀多么漂亮或其基礎(chǔ)架構(gòu)的架構(gòu)如何络拌。
領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)(“Domain-Driven Design領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)”簡(jiǎn)稱DDD)的哲學(xué)-由埃里克·埃文斯(Eric Evans)在他的同名書中首次描述-旨在將我們的注意力放在應(yīng)用程序的核心,專注于業(yè)務(wù)領(lǐng)域固有的復(fù)雜性本身回溺。我們還將核心域(對(duì)業(yè)務(wù)而言是唯一的)與支持子域(本質(zhì)上通常是通用的春贸,例如金錢或時(shí)間)區(qū)分開來,并將更多的設(shè)計(jì)工作適當(dāng)?shù)胤旁诤诵纳稀?/p>
過去系統(tǒng)分析和系統(tǒng)設(shè)計(jì)都是分離的遗遵,正如我們國(guó)家“系統(tǒng)分析師” 和“系統(tǒng)設(shè)計(jì)師” 兩種職稱考試一樣萍恕,這樣割裂的結(jié)果導(dǎo)致,需求分析的結(jié)果無法直接進(jìn)行設(shè)計(jì)編程瓮恭,而能夠進(jìn)行編程運(yùn)行的代碼卻扭曲需求雄坪,導(dǎo)致客戶運(yùn)行軟件后才發(fā)現(xiàn)很多功能不是自己想要的,而且軟件不能快速跟隨需求變化屯蹦。DDD則打破了這種隔閡维哈,提出了領(lǐng)域模型概念,統(tǒng)一了分析和設(shè)計(jì)編程登澜,使得軟件能夠更靈活快速跟隨需求變化阔挠。DDD包括一組用于從域模型構(gòu)建企業(yè)應(yīng)用程序的模式。
代碼和模型...
借助DDD脑蠕,我們正在尋求創(chuàng)建問題域的模型购撼。持久性,用戶界面和消息傳遞內(nèi)容可能會(huì)在以后出現(xiàn)谴仙,這是需要了解的領(lǐng)域迂求,因?yàn)檫@是所構(gòu)建系統(tǒng)中將您公司的業(yè)務(wù)與競(jìng)爭(zhēng)對(duì)手區(qū)分開來的部分。 (如果事實(shí)并非如此晃跺,則可以考慮購(gòu)買包裝產(chǎn)品)揩局。
通過模型,我們不是指一個(gè)圖或一組圖掀虎。當(dāng)然凌盯,圖是有用的付枫,但它們不是模型,只是模型的不同視圖(參見圖)驰怎。
? 模型與模型視圖
所以模型是我們選擇在軟件中實(shí)現(xiàn)的概念集阐滩,以代碼和用于構(gòu)建交付系統(tǒng)的任何其他軟件工件表示。換句話說县忌,代碼本身就是模型掂榔。文本編輯器提供了使用此模型的一種方法,盡管現(xiàn)代工具也提供了許多其他可視化效果(UML類圖芹枷,實(shí)體關(guān)系圖衅疙,Spring beandocs [2],Struts / JSF流程等)鸳慈。
這是DDD模式的第一個(gè):模型驅(qū)動(dòng)的設(shè)計(jì)饱溢。 這意味著能夠?qū)⒛P椭械母拍睿ɡ硐肭闆r下完全按字面意義)映射到設(shè)計(jì)/代碼的概念。 模型的改變意味著代碼的改變走芋。 更改代碼意味著模型已更改绩郎。 DDD并不要求您使用面向?qū)ο髮?duì)域進(jìn)行建模-例如,我們可以使用規(guī)則引擎來構(gòu)建模型-但鑒于主要的企業(yè)編程語言是基于OO的翁逞,因此大多數(shù)模型本質(zhì)上都是OO肋杖。 畢竟,OO是基于建模范例的挖函。 模型的概念將表示為類和接口状植,職責(zé)將表示為類成員。
溝通語言
現(xiàn)在讓我們看一下域驅(qū)動(dòng)設(shè)計(jì)的另一個(gè)基本原理怨喘〗蚧回顧一下:我們想構(gòu)建一個(gè)域模型來捕獲正在構(gòu)建的系統(tǒng)的問題域,并且我們將在代碼/軟件工件中表達(dá)這種理解必怜。為了幫助我們做到這一點(diǎn)肉拓,DDD提倡領(lǐng)域?qū)<液烷_發(fā)人員使用模型中的概念有意識(shí)地進(jìn)行交流。因此梳庆,域?qū)<也粫?huì)根據(jù)屏幕或菜單項(xiàng)上的字段來描述新的用戶故事暖途,而是談?wù)撚驅(qū)ο笏璧幕A(chǔ)屬性或行為。同樣膏执,開發(fā)人員也不會(huì)談?wù)摂?shù)據(jù)庫(kù)表中類或列的新實(shí)例變量驻售。
嚴(yán)格執(zhí)行此操作,我們將開發(fā)一種無處不在的語言更米。如果無法輕松表達(dá)一個(gè)想法芋浮,則表示領(lǐng)域模型中缺少一個(gè)概念,團(tuán)隊(duì)將共同努力找出該概念是什么壳快。一旦建立了該概念纸巷,屏幕就會(huì)出現(xiàn)一個(gè)領(lǐng)域或數(shù)據(jù)庫(kù)表的列上的新字段便會(huì)隨之出現(xiàn)。
像大多數(shù)DDD一樣眶痰,這種開發(fā)無處不在的語言的想法并不是一個(gè)真正的新想法:XPers稱其為“名稱系統(tǒng)”瘤旨,并且DBA多年來一直將數(shù)據(jù)字典放在一起。但是無處不在的語言是一個(gè)令人回味的術(shù)語竖伯,并且可以出售給商務(wù)和技術(shù)人員〈嬲埽現(xiàn)在,“整個(gè)團(tuán)隊(duì)”敏捷實(shí)踐已成為主流七婴,這也變得很有道理祟偷。
模型和上下文...
每當(dāng)我們討論模型時(shí),它總是在一定范圍內(nèi)打厘。通承蕹Γ可以從使用該系統(tǒng)的最終用戶集合中推斷出此上下文。因此户盯,我們有一個(gè)部署到交易員的前臺(tái)交易系統(tǒng)嵌施,或一個(gè)超市收銀員使用的銷售點(diǎn)系統(tǒng)。這些用戶以特定的方式與模型的概念相關(guān)莽鸭,并且模型的術(shù)語對(duì)這些用戶有意義吗伤,但對(duì)于上下文之外的任何其他人則不一定。 DDD將此稱為有界上下文(BC)硫眨。每個(gè)域模型僅存在于一個(gè)有界上下文中足淆,而有界上下文恰好包含一個(gè)域模型。
我必須承認(rèn)礁阁,當(dāng)我第一次了解有界上下文時(shí)巧号,我看不出要點(diǎn):如果BC與域模型同構(gòu),為什么要引入一個(gè)新術(shù)語氮兵?如果只有最終用戶與BC進(jìn)行交互裂逐,那么這個(gè)術(shù)語也許就不需要了。但是泣栈,不同的系統(tǒng)(BC)也彼此交互卜高,發(fā)送文件,傳遞消息南片,調(diào)用API等掺涛。如果我們知道有兩個(gè)BC相互交互,則我們必須注意在一個(gè)概念之間進(jìn)行轉(zhuǎn)換:域和其他域疼进。
在模型周圍放置明確的邊界還意味著我們可以開始討論這些BC之間的關(guān)系薪缆。實(shí)際上,DDD標(biāo)識(shí)了BC之間的一整套關(guān)系伞广,以便我們可以合理化在需要將不同的BC鏈接在一起時(shí)應(yīng)該采取的措施:
- 已發(fā)布的語言:交互的BC商定一種共同的語言(例如拣帽,企業(yè)服務(wù)總線上的一堆XML模式)疼电,通過它們它們可以彼此交互。
- 開放主機(jī)服務(wù):BC指定任何其他BC可以使用其服務(wù)的協(xié)議(例如RESTful Web服務(wù))减拭;
- 共享內(nèi)核:兩個(gè)BC使用通用的代碼內(nèi)核(例如蔽豺,庫(kù))作為通用的通用語言,但否則以其自己的特定方式來完成其他工作拧粪;
- 客戶/供應(yīng)商:一個(gè)BC使用另一個(gè)服務(wù)的服務(wù)修陡,并且是另一個(gè)BC的利益相關(guān)者(客戶)。因此可霎,它可以影響該BC提供的服務(wù)魄鸦;
- 遵循者:一個(gè)BC使用另一個(gè)服務(wù),但不是該另一個(gè)BC的利益相關(guān)者癣朗。因此拾因,它使用“原樣”(符合)該BC提供的協(xié)議或API;
- 反腐敗層:一個(gè)BC使用另一方的服務(wù)斯棒,而不是利益相關(guān)者盾致,但其目的是通過引入一組適配器將一個(gè)BC依賴的BC的變化所產(chǎn)生的影響降至最低,即反腐敗層荣暮。使用反腐敗層庭惜,可以降低依賴風(fēng)險(xiǎn),特別是在與舊系統(tǒng)集成時(shí)穗酥,至少實(shí)現(xiàn)反腐敗層比重新實(shí)現(xiàn)該舊系統(tǒng)性價(jià)比要高很多护赊。
DDD建議我們繪制一個(gè)上下文圖,以識(shí)別我們的BC和我們依賴或依賴的BC砾跃,并確定這些依賴的性質(zhì)骏啰。
所有有關(guān)上下文映射和BC的討論有時(shí)都稱為戰(zhàn)略DDD,這是有充分理由的抽高。畢竟判耕,考慮一下BC之間的關(guān)系是很政治的:我的系統(tǒng)將依賴于哪個(gè)上游系統(tǒng),我是否容易與它們集成翘骂,我對(duì)它們有影響力壁熄,我信任它們嗎?下游也是如此:哪些系統(tǒng)將使用我的服務(wù)碳竟,我如何將我的功能作為服務(wù)公開草丧,它們對(duì)我有影響?一旦產(chǎn)生誤解莹桅,應(yīng)用程序很容易失敗昌执。
圖層和六邊形
現(xiàn)在,我們開始向內(nèi)考慮我們自己的BC(系統(tǒng))的體系結(jié)構(gòu)。從根本上講懂拾,DDD只真正關(guān)心域?qū)印?/p>
當(dāng)然煤禽,我們多年來一直在構(gòu)建多層系統(tǒng),但這并不意味著我們一定很擅長(zhǎng)委粉。確實(shí)呜师,過去有一些主導(dǎo)技術(shù)-比如EJB ,所有業(yè)務(wù)邏輯似乎都滲透到應(yīng)用程序?qū)由踔帘硎緦又屑纸冢粝乱唤M數(shù)據(jù)實(shí)體類作為數(shù)據(jù)持有者,也就是所謂的貧血模型衷畦。這不是DDD的意思栗涂。
分層架構(gòu)是一種歷史悠久的架構(gòu),通過分層架構(gòu)祈争,可以將系統(tǒng)按不同職責(zé)組織成有序?qū)哟谓锍蹋捎谶@種劃分往往比較容易界定,也算是最常見和最受歡迎的一種架構(gòu)菩混,有一個(gè)說法是:“如果你不知道要用什么架構(gòu)忿墅,那就用它。
當(dāng)我們說一個(gè)系統(tǒng)是分層架構(gòu)的時(shí)候沮峡,你可以把這個(gè)軟件想象成一個(gè)有很多層的蛋糕的樣子疚脐,其中每一層放在它的下一層上。較高層使用諸多較低層定義和提供的服務(wù)邢疙,但較低層并沒有察覺較高層的存在棍弄。另外,每一層都會(huì)對(duì)其上層隱藏更低的層疟游。
分層體系結(jié)構(gòu)的一個(gè)缺點(diǎn)是呼畸,它建議線性依賴關(guān)系的堆疊,從表示層一直到基礎(chǔ)結(jié)構(gòu)層颁虐。但是蛮原,我們可能希望在表示層和基礎(chǔ)結(jié)構(gòu)層中支持不同的實(shí)現(xiàn)。
因此另绩,不是將我們的應(yīng)用程序視為一組圖層儒陨,而是將其視為六邊形,如圖所示板熊。六邊形的內(nèi)部代表了application和domain層框全。外部代表應(yīng)用的驅(qū)動(dòng)邏輯、基礎(chǔ)設(shè)施或其他應(yīng)用干签。內(nèi)部通過端口和外部系統(tǒng)通信津辩,端口代表了一定協(xié)議,以API呈現(xiàn)。
圖:六角結(jié)構(gòu)
按照領(lǐng)域分層的模型喘沿,在應(yīng)用層和領(lǐng)域?qū)觾?nèi)置后闸度,一個(gè)典型的六邊形架構(gòu)應(yīng)用有兩個(gè)口子,一個(gè)入口對(duì)應(yīng)用戶接口層蚜印,用于應(yīng)用控制莺禁,一個(gè)出口對(duì)應(yīng)數(shù)據(jù)訪問層,用于數(shù)據(jù)獲取和持久化窄赋。每個(gè)口子都可以對(duì)應(yīng)幾個(gè)適配器哟冬,該應(yīng)用可以被自動(dòng)化測(cè)試,系統(tǒng)層面的回歸測(cè)試忆绰,用戶交互操作浩峡,遠(yuǎn)程HTTP調(diào)用,REST調(diào)用或者其他错敢。在數(shù)據(jù)方面翰灾,通過配置使用外部的數(shù)據(jù)庫(kù),可以是Oracle數(shù)據(jù)庫(kù)稚茅,mock的數(shù)據(jù)庫(kù)纸淮,測(cè)試數(shù)據(jù)庫(kù)或生產(chǎn)數(shù)據(jù)庫(kù),從而實(shí)現(xiàn)應(yīng)用和外部數(shù)據(jù)庫(kù)的解耦亚享。
另外值得一提的是咽块,在六邊形架構(gòu)中,自動(dòng)化測(cè)試和用戶具有同等的地位虹蒋,在實(shí)現(xiàn)用戶界面的同時(shí)就需要考慮自動(dòng)化測(cè)試糜芳。它們對(duì)應(yīng)相同的端口。六邊形架構(gòu)不僅讓自動(dòng)化測(cè)試這件事情成為設(shè)計(jì)第一要素魄衅,同時(shí)自動(dòng)化測(cè)試也保證應(yīng)用邏輯不會(huì)泄露到用戶界面峭竣,在技術(shù)上保證了層次的分界。