DDD 領(lǐng)域驅(qū)動設(shè)計學(xué)習(xí)(四)- 架構(gòu)(分層/六邊形/RESTful)

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ù)軟件的核心祠锣。


DDD分層架構(gòu)

不過從分層架構(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)模型就變成如下所示:


DIP分層

采用了依賴注入方式后,其實可以發(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)主到。

六邊形架構(gòu)

六邊形架構(gòu)

按照領(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)移叨粘。分解開來:

  1. Resource:資源,即數(shù)據(jù)(前面說過網(wǎng)絡(luò)的核心)瘤睹。比如 newsfeed升敲,friends等;
  2. Representational:某種表現(xiàn)形式轰传,比如用JSON冻晤,XML,JPEG等绸吸;
  3. 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)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末倍啥,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子澎埠,更是在濱河造成了極大的恐慌虽缕,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,198評論 6 514
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蒲稳,死亡現(xiàn)場離奇詭異氮趋,居然都是意外死亡,警方通過查閱死者的電腦和手機江耀,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,334評論 3 398
  • 文/潘曉璐 我一進店門凭峡,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人决记,你說我怎么就攤上這事摧冀。” “怎么了?”我有些...
    開封第一講書人閱讀 167,643評論 0 360
  • 文/不壞的土叔 我叫張陵索昂,是天一觀的道長建车。 經(jīng)常有香客問我,道長椒惨,這世上最難降的妖魔是什么缤至? 我笑而不...
    開封第一講書人閱讀 59,495評論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮康谆,結(jié)果婚禮上领斥,老公的妹妹穿的比我還像新娘。我一直安慰自己沃暗,他們只是感情好月洛,可當我...
    茶點故事閱讀 68,502評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著孽锥,像睡著了一般嚼黔。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上惜辑,一...
    開封第一講書人閱讀 52,156評論 1 308
  • 那天唬涧,我揣著相機與錄音,去河邊找鬼盛撑。 笑死碎节,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的抵卫。 我是一名探鬼主播狮荔,決...
    沈念sama閱讀 40,743評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼陌僵!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起创坞,我...
    開封第一講書人閱讀 39,659評論 0 276
  • 序言:老撾萬榮一對情侶失蹤碗短,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后题涨,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體偎谁,經(jīng)...
    沈念sama閱讀 46,200評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,282評論 3 340
  • 正文 我和宋清朗相戀三年纲堵,在試婚紗的時候發(fā)現(xiàn)自己被綠了巡雨。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,424評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡席函,死狀恐怖铐望,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤正蛙,帶...
    沈念sama閱讀 36,107評論 5 349
  • 正文 年R本政府宣布督弓,位于F島的核電站,受9級特大地震影響乒验,放射性物質(zhì)發(fā)生泄漏愚隧。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,789評論 3 333
  • 文/蒙蒙 一锻全、第九天 我趴在偏房一處隱蔽的房頂上張望狂塘。 院中可真熱鬧,春花似錦鳄厌、人聲如沸荞胡。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,264評論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽硝训。三九已至,卻和暖如春新思,著一層夾襖步出監(jiān)牢的瞬間窖梁,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,390評論 1 271
  • 我被黑心中介騙來泰國打工夹囚, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留纵刘,地道東北人。 一個月前我還...
    沈念sama閱讀 48,798評論 3 376
  • 正文 我出身青樓荸哟,卻偏偏與公主長得像假哎,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子鞍历,可洞房花燭夜當晚...
    茶點故事閱讀 45,435評論 2 359

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