當(dāng)今的企業(yè)應(yīng)用程序無疑是復(fù)雜的,并且依靠某些專門技術(shù)(持久性,AJAX,Web服務(wù)等)來完成其工作。作為開發(fā)人員,我們傾向于專注于這些技術(shù)細(xì)節(jié)是可以理解的。但是事實(shí)是,不能解決業(yè)務(wù)需求的系統(tǒng)對任何人都沒有用,無論它的外觀多么漂亮或其基礎(chǔ)架構(gòu)的架構(gòu)如何。
領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)(“Domain-Driven Design領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)”簡稱DDD)的哲學(xué)-由埃里克·埃文斯(Eric Evans)在他的同名書中首次描述-旨在將我們的注意力放在應(yīng)用程序的核心,專注于業(yè)務(wù)領(lǐng)域固有的復(fù)雜性本身。我們還將核心域(對業(yè)務(wù)而言是唯一的)與支持子域(本質(zhì)上通常是通用的,例如金錢或時(shí)間)區(qū)分開來,并將更多的設(shè)計(jì)工作適當(dāng)?shù)胤旁诤诵纳稀?/p>
過去系統(tǒng)分析和系統(tǒng)設(shè)計(jì)都是分離的,正如我們國家“系統(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ù)與競爭對手區(qū)分開來的部分。 (如果事實(shí)并非如此,則可以考慮購買包裝產(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ū)ο髮τ蜻M(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ā)人員使用模型中的概念有意識地進(jìn)行交流。因此,域?qū)<也粫?huì)根據(jù)屏幕或菜單項(xiàng)上的字段來描述新的用戶故事,而是談?wù)撚驅(qū)ο笏璧幕A(chǔ)屬性或行為。同樣,開發(fā)人員也不會(huì)談?wù)摂?shù)據(jù)庫表中類或列的新實(shí)例變量。
嚴(yán)格執(zhí)行此操作,我們將開發(fā)一種無處不在的語言。如果無法輕松表達(dá)一個(gè)想法,則表示領(lǐng)域模型中缺少一個(gè)概念,團(tuán)隊(duì)將共同努力找出該概念是什么。一旦建立了該概念,屏幕就會(huì)出現(xiàn)一個(gè)領(lǐng)域或數(shù)據(jù)庫表的列上的新字段便會(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)。通??梢詮氖褂迷撓到y(tǒng)的最終用戶集合中推斷出此上下文。因此,我們有一個(gè)部署到交易員的前臺交易系統(tǒng),或一個(gè)超市收銀員使用的銷售點(diǎn)系統(tǒng)。這些用戶以特定的方式與模型的概念相關(guān),并且模型的術(shù)語對這些用戶有意義,但對于上下文之外的任何其他人則不一定。 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)識了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)核(例如,庫)作為通用的通用語言,但否則以其自己的特定方式來完成其他工作;
- 客戶/供應(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è)上下文圖,以識別我們的BC和我們依賴或依賴的BC,并確定這些依賴的性質(zhì)。
所有有關(guān)上下文映射和BC的討論有時(shí)都稱為戰(zhàn)略DDD,這是有充分理由的。畢竟,考慮一下BC之間的關(guān)系是很政治的:我的系統(tǒng)將依賴于哪個(gè)上游系統(tǒng),我是否容易與它們集成,我對它們有影響力,我信任它們嗎?下游也是如此:哪些系統(tǒng)將使用我的服務(wù),我如何將我的功能作為服務(wù)公開,它們對我有影響?一旦產(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),但這并不意味著我們一定很擅長。確實(shí),過去有一些主導(dǎo)技術(shù)-比如EJB ,所有業(yè)務(wù)邏輯似乎都滲透到應(yīng)用程序?qū)由踔帘硎緦又?,留下一組數(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ì)對其上層隱藏更低的層。
分層體系結(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è)入口對應(yīng)用戶接口層,用于應(yīng)用控制,一個(gè)出口對應(yīng)數(shù)據(jù)訪問層,用于數(shù)據(jù)獲取和持久化。每個(gè)口子都可以對應(yīng)幾個(gè)適配器,該應(yīng)用可以被自動(dòng)化測試,系統(tǒng)層面的回歸測試,用戶交互操作,遠(yuǎn)程HTTP調(diào)用,REST調(diào)用或者其他。在數(shù)據(jù)方面,通過配置使用外部的數(shù)據(jù)庫,可以是Oracle數(shù)據(jù)庫,mock的數(shù)據(jù)庫,測試數(shù)據(jù)庫或生產(chǎn)數(shù)據(jù)庫,從而實(shí)現(xiàn)應(yīng)用和外部數(shù)據(jù)庫的解耦。
另外值得一提的是,在六邊形架構(gòu)中,自動(dòng)化測試和用戶具有同等的地位,在實(shí)現(xiàn)用戶界面的同時(shí)就需要考慮自動(dòng)化測試。它們對應(yīng)相同的端口。六邊形架構(gòu)不僅讓自動(dòng)化測試這件事情成為設(shè)計(jì)第一要素,同時(shí)自動(dòng)化測試也保證應(yīng)用邏輯不會(huì)泄露到用戶界面,在技術(shù)上保證了層次的分界。