DDD方法中并沒有指定使用特定的架構(gòu)。領(lǐng)域中的BC被封裝為高內(nèi)聚的模塊嘹悼,這種特性讓DDD對架構(gòu)并沒有太大侵入性叛甫。架構(gòu)可以應(yīng)用于領(lǐng)域內(nèi)部的結(jié)構(gòu),也可以包圍著領(lǐng)域模型杨伙,系統(tǒng)中可以采用多種風格的架構(gòu)其监。
架構(gòu)是指構(gòu)成一個系統(tǒng)的主要元素及它們之間的主要關(guān)聯(lián),這些元素和關(guān)聯(lián)能夠反映該系統(tǒng)的本質(zhì)特征限匣。
選擇架構(gòu)應(yīng)該了解架構(gòu)的來源和所要解決的問題抖苦,從業(yè)務(wù)和問題出發(fā),避免濫用架構(gòu)膛腐。例如分布式應(yīng)用的架構(gòu)有REST睛约、DO(分布式對象)和RPC,REST架構(gòu)是Web風格的架構(gòu)哲身,而DO和RPC是面向企業(yè)應(yīng)用的辩涝,技術(shù)和架構(gòu)也不是一個概念垄琐,例如用了HTTP不一定就是Web架構(gòu)顿颅。
以建筑為例龙助,蘇州園林是一種建筑風格琼稻,歐洲的哥特式教堂也是一鐘建筑風格,在蘇州園林中放個哥特式教堂就讓讓人感覺畫風突兀商膊。
分層架構(gòu)
分層架構(gòu)是一種歷史悠久的架構(gòu)伏伐,通過分層架構(gòu),可以將系統(tǒng)按不同職責組織成有序?qū)哟卧尾穑捎谶@種劃分往往比較容易界定藐翎,也算是最常見和最受歡迎的一種架構(gòu),有一個說法是:“如果你不知道要用什么架構(gòu)实幕,那就用它吝镣。
Fowler在《Patterns of Enterprise Application Architecture》中對分層的定義是:
當我們說一個系統(tǒng)是分層架構(gòu)的時候,你可以把這個軟件想象成一個有很多層的蛋糕的樣子昆庇,其中每一層放在它的下一層上末贾。較高層使用諸多較低層定義和提供的服務(wù),但較低層并沒有察覺較高層的存在整吆。另外拱撵,每一層都會對其上層隱藏更低的層。
介紹分層的文章和例子都比較多表蝙,不再贅述拴测,簡要說下DDD中的分層模型。
在分層架構(gòu)中勇哗,我們將領(lǐng)域模型和業(yè)務(wù)邏輯分離出來昼扛,并減少對基礎(chǔ)設(shè)施、用戶界面甚至應(yīng)用層邏輯的依賴欲诺,因為它們不屬于業(yè)務(wù)邏抄谐。將一個復(fù)雜的系統(tǒng)分為不同的層,每層都應(yīng)該具有良好的內(nèi)聚性扰法,并且只依賴于比其自身更低的層蛹含。[Evans, Ref, P16]
一個DDD中的分層架構(gòu)可以如下圖所示,通過把領(lǐng)域?qū)訂为毞蛛x出來塞颁,負責表達業(yè)務(wù)概念浦箱,業(yè)務(wù)狀態(tài)以及業(yè)務(wù)規(guī)則,形成對領(lǐng)域知識的集中并形成業(yè)務(wù)軟件的核心祠锣。
不過從分層架構(gòu)圖中可以發(fā)現(xiàn)酷窥,將基礎(chǔ)設(shè)施層放入底層是存在缺點的,領(lǐng)域?qū)右蕾囉诨A(chǔ)設(shè)施層伴网,這對領(lǐng)域?qū)拥膬?nèi)聚性產(chǎn)生影響蓬推。一個解決方案就是依賴倒置。
依賴倒置
依賴倒置的原則(DIP)由Robert C. Martin提出澡腾,核心的定義是:
高層模塊不應(yīng)該依賴于底層模塊沸伏,兩者都應(yīng)該依賴于抽象
抽象不應(yīng)該依賴于實現(xiàn)細節(jié)糕珊,實現(xiàn)細節(jié)應(yīng)該依賴于接口
按照DIP的原則,領(lǐng)域?qū)泳涂梢圆辉僖蕾囉诨A(chǔ)設(shè)施層毅糟,基礎(chǔ)設(shè)施層通過注入持久化的實現(xiàn)就完成了對領(lǐng)域?qū)拥慕怦詈煅。捎靡蕾囎⑷朐瓌t的新分層架構(gòu)模型就變成如下所示:
采用了依賴注入方式后,其實可以發(fā)現(xiàn)事實上已經(jīng)沒有分層概念了姆另。無論高層還是底層喇肋,實際只依賴于抽象,整個分層好像被推平了迹辐,這就引入下一個架構(gòu)六邊形架構(gòu)苟蹈。
六邊形架構(gòu)(Hexagonal architecture)
六邊形架構(gòu)是Alistair Cockburn在2005年提出,解決了傳統(tǒng)的分層架構(gòu)所帶來的問題右核,實際上它也是一種分層架構(gòu),只不過不是上下或左右渺绒,而是變成了內(nèi)部和外部贺喝。在《實現(xiàn)領(lǐng)域驅(qū)動設(shè)計》一書中,作者將六邊形架構(gòu)應(yīng)用到領(lǐng)域驅(qū)動設(shè)計的實現(xiàn)宗兼,六邊形的內(nèi)部代表了application和domain層躏鱼。外部代表應(yīng)用的驅(qū)動邏輯、基礎(chǔ)設(shè)施或其他應(yīng)用殷绍。內(nèi)部通過端口和外部系統(tǒng)通信染苛,端口代表了一定協(xié)議,以API呈現(xiàn)主到。
按照領(lǐng)域分層的模型茶行,在應(yīng)用層和領(lǐng)域?qū)觾?nèi)置后,一個典型的六邊形架構(gòu)應(yīng)用有兩個端口登钥,一個端口對應(yīng)用戶接口層畔师,用于應(yīng)用控制,一個對應(yīng)數(shù)據(jù)訪問層牧牢,用于數(shù)據(jù)獲取和持久化看锉。每個端口都可以對應(yīng)幾個適配器,該應(yīng)用可以被自動化測試塔鳍,系統(tǒng)層面的回歸測試伯铣,用戶交互操作,遠程HTTP調(diào)用轮纫,REST調(diào)用或者其他腔寡。在數(shù)據(jù)方面,通過配置使用外部的數(shù)據(jù)庫蜡感,可以是Oracle數(shù)據(jù)庫蹬蚁,mock的數(shù)據(jù)庫恃泪,測試數(shù)據(jù)庫或生產(chǎn)數(shù)據(jù)庫,從而實現(xiàn)應(yīng)用和外部數(shù)據(jù)庫的解耦犀斋。
另外值得一提的是贝乎,在六邊形架構(gòu)中,自動化測試和用戶具有同等的地位叽粹,在實現(xiàn)用戶界面的同時就需要考慮自動化測試览效。它們對應(yīng)相同的端口。六邊形架構(gòu)不僅讓自動化測試這件事情成為設(shè)計第一要素虫几,同時自動化測試也保證應(yīng)用邏輯不會泄露到用戶界面锤灿,在技術(shù)上保證了層次的分界。
使用六邊形架構(gòu)的時候辆脸,我們應(yīng)該根據(jù)用例來設(shè)計應(yīng)用程序但校,而不是需要支持的客戶數(shù)量來設(shè)計。任何客戶都可能向不同的端口發(fā)出請求啡氢,但是所有的適配器都使用相同的API状囱。
六邊形架構(gòu)的功能非常強大,可以作為基層架構(gòu)并用于支持系統(tǒng)的其他架構(gòu)倘是。
面向服務(wù)架構(gòu)
SOA
略
REST
REST表述性狀態(tài)傳遞( Representational State Transfer)亭枷,是 Fielding博士在2000年他的博士論文中提出來的一種軟件架構(gòu)風格。
REST全稱是 Resource Representational State Transfer搀崭,通俗來講就是:資源在網(wǎng)絡(luò)中以某種表現(xiàn)形式進行狀態(tài)轉(zhuǎn)移叨粘。分解開來:
- Resource:資源,即數(shù)據(jù)(前面說過網(wǎng)絡(luò)的核心)瘤睹。比如 newsfeed升敲,friends等;
- Representational:某種表現(xiàn)形式轰传,比如用JSON冻晤,XML,JPEG等绸吸;
- State Transfer:狀態(tài)變化鼻弧。通過HTTP動詞實現(xiàn)。
作為一種被廣泛使用锦茁,甚至被濫用的架構(gòu)流行語攘轩,很多人了解REST是把它作為一種Web通信協(xié)議,但實際REST是這是一種架構(gòu)風格码俩。架構(gòu)風格之于架構(gòu)就像設(shè)計模式之于設(shè)計一樣度帮。它將不同架構(gòu)實現(xiàn)所共有的東西抽象出來,使得我們在談及到架構(gòu)時不至于陷人技術(shù)細節(jié)中。
作為一種架構(gòu)風格笨篷,就需要遵循其提出的一系列架構(gòu)級約束瞳秽。REST的約束有:
- 客戶-服務(wù)器(Client-Server),通信只能由客戶端單方面發(fā)起率翅,表現(xiàn)為請求-響應(yīng)的形式练俐。
- 無狀態(tài)(Stateless) 通信的會話狀態(tài)(Session State)應(yīng)該全部由客戶端負責維護。
- 緩存(Cache) 響應(yīng)內(nèi)容可以在通信鏈的某處被緩存冕臭,以改善網(wǎng)絡(luò)效率腺晾。
- 統(tǒng)一接口(Uniform Interface) 通信鏈的組件之間通過統(tǒng)一的接口相互通信,以提高交互的可見性辜贵。
- 分層系統(tǒng)(Layered System)通過限制組件的行為(即悯蝉,每個組件只能“看到”與其交互的緊鄰層),將架構(gòu)分解為若干等級的層托慨。
- 按需代碼(Code-On-Demand鼻由,可選)
如果一個系統(tǒng)滿足了上面所列出的約束,那么該系統(tǒng)就被稱為是RESTful的厚棵。當然一旦遵循了以上約束嗡靡,也意味著這個系統(tǒng)就會享受到這種架構(gòu)風格帶來的好處。實際上HTTP1.1也正是基于REST架構(gòu)風格來設(shè)計的窟感,REST架構(gòu)也是Web之所以取得成功的技術(shù)架構(gòu)方面因素的總結(jié)。
符合REST原則的系統(tǒng)將具有更好的松耦合性歉井。通常來講柿祈,添加新資源并在已有資源中創(chuàng)建到新資源的鏈接是非常簡單的。另外基于 REST的系統(tǒng)也是非常容易理解的哩至,因為此時系統(tǒng)被分為很多較小的資源塊躏嚎,每一個資源塊都可以獨立地測試和調(diào)試,并且每一個資源塊都表示了一個可重用的人口點菩貌。 HTTP設(shè)計本身以及URI成熟的重寫與緩存機制使得 RESTful HTTP成為一種不錯的架構(gòu)選擇卢佣,該架構(gòu)具有很好的松耦合性和可伸縮性。
REST和DDD
領(lǐng)域模型通過REST直接暴露給外界并不被推薦箭阶,要將 DDD與 RESTful HTTP合并起來使用虚茶,推薦的方式是:為系統(tǒng)接口層單獨創(chuàng)建一個限界上下文,再在此上下文中通過適當?shù)牟呗詠碓L問實際的核心模型仇参。它將系統(tǒng)接口看作一個整體嘹叫,通過資源抽象將系統(tǒng)功能暴露給外界,而不是通過服務(wù)或者遠程接口诈乒。
另一種方法用于需要使用標準媒體類型的時候罩扇。如果某種媒體類型并不用于支持單個系統(tǒng)接口,而是用于一組相似的客戶端.服務(wù)器交互場景,此時我們可以創(chuàng)建一個領(lǐng)域模型來處理每一種媒體類型喂饥。這樣的領(lǐng)域模型甚至可以在服務(wù)器和客戶端之間進行重用消约。
這也是一種由外向里的、橫切式的方法员帮。在上面提到的工作組例子中或粮,有多種常用的格式可以用于領(lǐng)域模型。比如 iCal格式(一種標準的互聯(lián)網(wǎng)日歷格式)集侯。我們首先選擇一種媒體類型被啼,即 iCal,然后再根據(jù)這種格式創(chuàng)建領(lǐng)域模型。該模型可以用于任何能夠理解 ical格式的系統(tǒng)棠枉,比如服務(wù)器程序浓体,或者 Android客戶端。在采用這種方法時辈讶,服務(wù)器需要處理多種類型的媒體類型命浴,而同一種媒體類型又可以用于多個服務(wù)器。
如何在以上兩種方法之間進行選取呢贱除?這在很大程度上取決于系統(tǒng)設(shè)計者對可重用性上的要求生闲。第一種方法比較適合更加專屬的系統(tǒng),而第二種方法更適合那些通用的系統(tǒng)月幌。
兩個建議:
- 設(shè)計好REST的API接口后碍讯,通過Application層去順序調(diào)用合適的領(lǐng)域?qū)ο螅@也正是Application層的價值所在扯躺。
- 不要盲目的遵循教條的理論捉兴。我們應(yīng)該盡可能遵循各種理論和最佳實踐,但是如果不行的時候就先把這些放到一邊去(當然這需要在慎重考慮后)录语。
參考資料:
DDD分層架構(gòu)的三種模式
微服務(wù)架構(gòu)基礎(chǔ)——解讀六邊形架構(gòu)
SOA面向服務(wù)架構(gòu)