在分布式系統(tǒng)中使用 DDD

在使用 DDD 的思想時(shí),最讓人迷惑的就是如何組織代碼,也就是通常所說(shuō)的系統(tǒng)架構(gòu)的問(wèn)題。在前面提到 DDD 可以很好地指導(dǎo)代碼組織,其中舉了兩個(gè)例子,單體和微服務(wù)架構(gòu)下 DDD 如何指導(dǎo)代碼的組織方式。令人沮喪的是,大部分應(yīng)用系統(tǒng)既不是完全的單體系統(tǒng),也不是純粹的微服務(wù)架構(gòu),而是出于某種中間狀態(tài)。

無(wú)論我們使用單體、SOA、微服務(wù)、中臺(tái)或者其他架構(gòu),都需要解決如何組織代碼這個(gè)問(wèn)題,DDD 并不是一個(gè)技術(shù),而是指導(dǎo)我們組織代碼的一種思想,這種思想也并不是憑空出現(xiàn)的。

就代碼組織這個(gè)問(wèn)題,看起來(lái)沒(méi)有什么技術(shù)含量,但實(shí)際上非常重要,軟件工程發(fā)展過(guò)程中出現(xiàn)過(guò)三次危機(jī),軟件危機(jī)泛指在計(jì)算機(jī)軟件的開(kāi)發(fā)和維護(hù)過(guò)程中所遇到的一系列嚴(yán)重問(wèn)題,代碼的組織和大規(guī)模協(xié)作是其重要的組成部分。

  1. 結(jié)構(gòu)化程序設(shè)計(jì)解決了第一次軟件危機(jī)。60年代~70年代計(jì)算機(jī)剛剛投入商業(yè)使用,主要的編程方式還是匯編語(yǔ)言在特定的機(jī)器上編寫(xiě)程序。當(dāng)軟件規(guī)模較小,基本上處于計(jì)算機(jī)科學(xué)家個(gè)人編碼設(shè)計(jì)、使用的方式。隨著軟件規(guī)模擴(kuò)大,復(fù)雜度增加,依賴(lài)特定機(jī)器、無(wú)結(jié)構(gòu)化的編程方式無(wú)法應(yīng)對(duì)軟件的發(fā)展,帶來(lái)了第一次軟件危機(jī)。為了克服這個(gè)問(wèn)題,業(yè)界提出了”軟件工程“的概念,1972 年 C 語(yǔ)言的出現(xiàn),解決了代碼結(jié)構(gòu)化、抽象性、可移植的問(wèn)題。
  2. 面向?qū)ο蠼鉀Q了第二次軟件危機(jī)。隨著軟件在商業(yè)中大規(guī)模使用,軟件變得原來(lái)越復(fù)雜,即使結(jié)構(gòu)化的 C 語(yǔ)言也無(wú)法滿(mǎn)足業(yè)界對(duì)可維護(hù)性、可拓展性的需求。標(biāo)志性的事件是 IBM 公司開(kāi)發(fā)的 OS/360 系統(tǒng)失敗,該系統(tǒng)有 4000 多個(gè)模塊,約 100 萬(wàn)條指令,以及大量的 bug。面向?qū)ο蟮木幊陶Z(yǔ)言,Java、C#、C++ 出現(xiàn),面向?qū)ο髱?lái)了更自然地代碼組織方式,軟件開(kāi)發(fā)變得越像建筑業(yè)。
  3. 第三次軟件危機(jī)。第三次軟件危機(jī)還沒(méi)有一個(gè)明確定義,通常來(lái)說(shuō)就是互聯(lián)網(wǎng)行業(yè)興起,軟件變得越來(lái)越復(fù)雜,需求越來(lái)越多變。軟件開(kāi)發(fā)從建筑業(yè)變成了服務(wù)業(yè),需要隨時(shí)響應(yīng)變化,在軟件行業(yè)表現(xiàn)為瀑布開(kāi)發(fā)越來(lái)越不可行,敏捷開(kāi)發(fā)越來(lái)越重要。從技術(shù)上表現(xiàn)為單機(jī)開(kāi)發(fā)越來(lái)越不可行,分布式系統(tǒng)是必然的趨勢(shì)。

每一次危機(jī)的解決,都是建立在前一次的基礎(chǔ)之上的。面向?qū)ο笫墙⒃诮Y(jié)構(gòu)化程序設(shè)計(jì)之上的,敏捷也是建立在瀑布之上的,而不是推翻前者。DDD 還停留在面向?qū)ο筮@個(gè)階段,可以用來(lái)指導(dǎo)分布式系統(tǒng)設(shè)計(jì),應(yīng)對(duì)越來(lái)越復(fù)雜的應(yīng)用系統(tǒng),DDD 也不是面向?qū)ο笏枷氲奶娲摺?/p>

DDD 的代碼組織形式眾說(shuō)紛紜,并沒(méi)有一個(gè)標(biāo)準(zhǔn)的代碼架構(gòu)。為什么會(huì)這樣呢?實(shí)踐中我們發(fā)現(xiàn),不同公司、項(xiàng)目的業(yè)務(wù)背景不一致,架構(gòu)不一致,架構(gòu)的演化層次不一樣(查看另外一篇文章《架構(gòu)的演進(jìn)》),標(biāo)準(zhǔn)的代碼架構(gòu)并不適合每一個(gè)公司。

當(dāng)我們的系統(tǒng)架構(gòu)從單體往 SOA、微服務(wù)、中臺(tái)演變,無(wú)論名稱(chēng)如何變化,實(shí)際上都是分布式系統(tǒng),只不過(guò)分布式的程度不一致而已。所以我們需要將問(wèn)題拓展到分布式系統(tǒng)這個(gè)更大的概念上,再來(lái)談 DDD 的代碼組織形式才有意義。

我們看一下分布式系統(tǒng)下一個(gè)定義:

分布式系統(tǒng)是一組電腦,透過(guò)網(wǎng)絡(luò)相互連接傳遞消息與通信后并協(xié)調(diào)它們的行為而形成的系統(tǒng)。——維基百科

從廣義的分布式系統(tǒng)定義上來(lái)看,現(xiàn)在的互聯(lián)網(wǎng)應(yīng)用基本上沒(méi)有不是分布式的了。分布式系統(tǒng)不是軟件工程師主動(dòng)選擇的結(jié)構(gòu),而是業(yè)務(wù)逼得這樣選擇。阿里巴巴帶動(dòng)的去 IOE (去掉IBM的小型機(jī)、Oracle數(shù)據(jù)庫(kù)、EMC存儲(chǔ)設(shè)備,代之以自己在開(kāi)源軟件基礎(chǔ)上開(kāi)發(fā)的系統(tǒng))就是一個(gè)很好的體現(xiàn)。

在這樣的一個(gè)思維方式下,單體系統(tǒng)是只有一個(gè)計(jì)算節(jié)點(diǎn)的分布式系統(tǒng),那么 DDD 在單體應(yīng)用下的經(jīng)驗(yàn)也可以應(yīng)用起來(lái)。我沒(méi)有找到一個(gè)專(zhuān)業(yè)術(shù)語(yǔ)描述分布式系統(tǒng)程度,這里請(qǐng)?jiān)试S我創(chuàng)造一個(gè)新詞,分布式級(jí)別

分布式級(jí)別

為了解決業(yè)務(wù)上的問(wèn)題,用戶(hù)量大、業(yè)務(wù)規(guī)模大,當(dāng)用戶(hù)量增長(zhǎng)到無(wú)法被容忍時(shí),我們引入分庫(kù)分表(分布式數(shù)據(jù)庫(kù))、垂直拆分業(yè)務(wù)(微服務(wù))。

我們會(huì)將系統(tǒng)變得越來(lái)越復(fù)雜,然后不得不解決各種分布式系統(tǒng)下的新問(wèn)題,業(yè)務(wù)上面臨的問(wèn)題被轉(zhuǎn)移到技術(shù)上,從而業(yè)務(wù)才有可能持續(xù)性的發(fā)展。我們面臨的問(wèn)題不會(huì)消失,只會(huì)從一個(gè)地方轉(zhuǎn)移到另外一個(gè)地方,轉(zhuǎn)移到我們能容忍的地方,比如轉(zhuǎn)移到云上,然后通過(guò)購(gòu)買(mǎi)服務(wù)解決。

系統(tǒng)中節(jié)點(diǎn)角色越少,需要解決的分布式問(wèn)題則越少,可以認(rèn)為這是低級(jí)別的分布式系統(tǒng)。低級(jí)別的分布式系統(tǒng) 架構(gòu)基本上沒(méi)有什么分布式問(wèn)題存在,目前主流的小項(xiàng)目通過(guò) Nginx 讓?xiě)?yīng)用水平拓展 + 主從數(shù)據(jù)庫(kù)的架構(gòu)可以看做低級(jí)別的分布式系統(tǒng)。

系統(tǒng)中節(jié)點(diǎn)的角色越多,應(yīng)用垂直拆分,需要解決的分布式問(wèn)題就越多,遇到的技術(shù)挑戰(zhàn)也越多,我們可以認(rèn)為這是高級(jí)別的的系統(tǒng)。應(yīng)用系統(tǒng)的例子就是微服務(wù)架構(gòu),另外一個(gè)例子就是大數(shù)據(jù)平臺(tái)。

我把分布式級(jí)別做了如下劃分,基本上可以囊括目前互聯(lián)網(wǎng)應(yīng)用系統(tǒng)的主流架構(gòu):

  • 準(zhǔn)單體系統(tǒng)
  • 低級(jí)別分布式系統(tǒng)
  • 高級(jí)別分布式系統(tǒng)
  • 復(fù)雜分布式系統(tǒng)

在微服務(wù)項(xiàng)目中經(jīng)歷過(guò)痛苦的開(kāi)發(fā)者應(yīng)該所有體會(huì),全世界開(kāi)發(fā)者貢獻(xiàn)了大量的開(kāi)源軟件嘗試解決這些問(wèn)題,后面詳細(xì)介紹每一個(gè)問(wèn)題如何具體解決。

清醒的使用 DDD

上面這些分布式系統(tǒng)的問(wèn)題,DDD 都解決不了。DDD 的作用只有一個(gè):在單體中劃分模塊,在分布式系統(tǒng)中劃分服務(wù)。 服務(wù)劃分的良好,關(guān)聯(lián)查詢(xún)、授權(quán)、分布式一致性等問(wèn)題可以被很好的解決,也就是我們常常說(shuō)的解耦

但是就這一個(gè)作用,對(duì)于做應(yīng)用開(kāi)發(fā)的業(yè)務(wù)系統(tǒng)來(lái)說(shuō)至關(guān)重要,雖然對(duì)于專(zhuān)門(mén)解決技術(shù)復(fù)雜度問(wèn)題的云廠商來(lái)說(shuō)用處不大,所以最好讓 DDD 在合適的地方發(fā)揮作用。高級(jí)別的微服務(wù)系統(tǒng)的修改成本如此之高,以至于服務(wù)劃分錯(cuò)誤幾乎沒(méi)有能力調(diào)整回來(lái),甚至導(dǎo)致很多互聯(lián)網(wǎng)公司就此走向失敗。

因此,如何劃分服務(wù),這是 DDD 非常有價(jià)值的一個(gè)地方,在分布式系統(tǒng)中,DDD 起到的作用實(shí)際上就是指導(dǎo)垂直拓展。值得慶幸的是,應(yīng)用系統(tǒng)分布式級(jí)別增加帶來(lái)很多技術(shù)挑戰(zhàn),但是邏輯上的架構(gòu)變化卻不大。

在每一個(gè)不同的演化層次下,談 DDD 的代碼架構(gòu)才有意義。例如單體系統(tǒng)沒(méi)有必要過(guò)多分層,避免樣板代碼大量出現(xiàn);微服務(wù)系統(tǒng)則需要小心分層,并嚴(yán)格執(zhí)行,否則修改成本非常高。另外也需要解決該層次下的技術(shù)問(wèn)題,微服務(wù)需要解決分布式事務(wù)問(wèn)題、分布式授權(quán)問(wèn)題、分布式緩存問(wèn)題、性能問(wèn)題等。

DDD 分層和職責(zé)

在 DDD 指導(dǎo)代碼設(shè)計(jì)部分,我們提到了三層架構(gòu)和 DDD 的四層架構(gòu)的區(qū)別,DDD 的四層架構(gòu)被越來(lái)越多的認(rèn)可,但是每層具體的職責(zé)很少有文章談到。根據(jù)實(shí)踐經(jīng)驗(yàn),我把四層模型中具體的職責(zé)整理出來(lái),用于團(tuán)隊(duì)在做架構(gòu)設(shè)計(jì)中能有共同的認(rèn)識(shí)。

前面的 DDD 四層模型的圖為了表達(dá)每層中的元素,丟失了一個(gè)重要的角度,每一層的組件可能有多個(gè)。還是以收銀機(jī)系統(tǒng)為例,架構(gòu)會(huì)是像下面這樣,業(yè)界大多數(shù)互聯(lián)網(wǎng)架構(gòu)圖也是這樣畫(huà)的,只是使用術(shù)語(yǔ)略有不同。

實(shí)踐中我們發(fā)現(xiàn),接入層是由應(yīng)用場(chǎng)景解決的,因此接入層需要在特定應(yīng)用場(chǎng)景下使用。收銀機(jī)應(yīng)用下,接入層是 Restful API 以及 socket 連接實(shí)現(xiàn)的實(shí)時(shí)通信,商戶(hù)管理和平臺(tái)管理無(wú)需使用這些接入方法,在不前后端分離的情況下,模板引擎也足夠使用。

同樣的,基礎(chǔ)設(shè)施層是和領(lǐng)域?qū)咏壎ǖ揭黄鹩糜趯?shí)現(xiàn)業(yè)務(wù)邏輯和規(guī)則,底層基礎(chǔ)設(shè)施的選擇由領(lǐng)域?qū)記Q定。商品服務(wù)主要是和數(shù)據(jù)庫(kù)打交道,需要使用 Mybatis,但是用戶(hù)認(rèn)證服務(wù)(圖上未體現(xiàn))可能只需要 Redis 做分布式會(huì)話(huà)即可。

接入層和技術(shù)設(shè)施層,更應(yīng)該看做兩個(gè)亞層。結(jié)合 DDD 術(shù)語(yǔ)將示例圖調(diào)整如下:

應(yīng)用層

餐飲系統(tǒng)是一個(gè)非常復(fù)雜,具有多端、多租戶(hù)的系統(tǒng),往往有收銀機(jī)應(yīng)用、手機(jī)點(diǎn)餐應(yīng)用、商戶(hù)管理、平臺(tái)管理等應(yīng)用,從而組合成一個(gè)系統(tǒng)。在有些公司的語(yǔ)境里,應(yīng)用層往往是根據(jù)用戶(hù)角色劃分的,被稱(chēng)為”業(yè)務(wù)面“。

應(yīng)用層的特點(diǎn):

  • 關(guān)心處理完一個(gè)完整的業(yè)務(wù)
  • 該層只負(fù)責(zé)業(yè)務(wù)編排,對(duì)象轉(zhuǎn)換,實(shí)際業(yè)務(wù)邏輯由領(lǐng)域?qū)油瓿?/li>
  • 不關(guān)心請(qǐng)求從何處來(lái),但是關(guān)心誰(shuí)來(lái)、做什么、有沒(méi)有權(quán)限做
  • 集成不同的領(lǐng)域服務(wù)解決問(wèn)題
  • 最終一致性(最終一致性對(duì)業(yè)務(wù)有侵入)事務(wù)放到這層
  • 對(duì)應(yīng)到分布式系統(tǒng)中的中臺(tái)等概念
  • 方法級(jí)別的功能權(quán)限控制放到這層
  • 只產(chǎn)應(yīng)用異常,對(duì)應(yīng) HTTP 狀態(tài)碼 403、401
  • 準(zhǔn)單體系統(tǒng)下,按照應(yīng)用劃分模塊

接入層

對(duì)接入層來(lái)說(shuō),我們可以看到,實(shí)際上接入層是依附于應(yīng)用層存在的,隨著前后端分離,Restful API 成了主流,對(duì)簡(jiǎn)單的系統(tǒng)來(lái)說(shuō)這一層越來(lái)越弱化。對(duì)于有終端接入的系統(tǒng)來(lái)說(shuō),接入層并不簡(jiǎn)單,需要處理各種協(xié)議適配:XMPP、websocket、MQTT 等。在復(fù)雜度不高的情況下,我們往往把接入層和應(yīng)用層合并部署,這里往往憑經(jīng)驗(yàn)來(lái)決定。如果對(duì)分布式級(jí)別有了認(rèn)識(shí),可以更為科學(xué)的選擇是否要將接入層和應(yīng)用部署到一起。

接入層的特點(diǎn):

  • 關(guān)心視圖和對(duì)外的服務(wù),Restful、頁(yè)面渲染、websocket、XMPP 連接等
  • 如果沒(méi)有多種接入方式,可以和應(yīng)用層合并
  • 對(duì)應(yīng)到分布式系統(tǒng)中的網(wǎng)關(guān)、BFF、前臺(tái)等概念
  • 只產(chǎn)生接入異常,例如數(shù)據(jù)校驗(yàn),對(duì)應(yīng) HTTP 狀態(tài)碼 400、415 等
  • 一個(gè)應(yīng)用可以有多個(gè)接入層
  • 接入層做和業(yè)務(wù)規(guī)則無(wú)關(guān)的 bean validation 驗(yàn)證
  • 準(zhǔn)單體系統(tǒng)下,按照連接方式分包

領(lǐng)域?qū)?/h4>

對(duì)于領(lǐng)域?qū)觼?lái)說(shuō),很多互聯(lián)網(wǎng)公司沒(méi)有這個(gè)概念,將這些實(shí)現(xiàn)混合在應(yīng)用層隱藏實(shí)現(xiàn)了,造成業(yè)務(wù)規(guī)則不一致。隨著前后端分離的發(fā)展,2013 年左右我也開(kāi)始前后端分離實(shí)踐,接入層剝離出去后,后端開(kāi)發(fā)者開(kāi)始審視是否需要抽象出一層來(lái)復(fù)用業(yè)務(wù)邏輯。當(dāng)時(shí)大部分互聯(lián)網(wǎng)公司稱(chēng)為服務(wù),也就是 SOA 架構(gòu),大量使用 XML 和 SOAP 技術(shù)。

領(lǐng)域?qū)拥奶攸c(diǎn):

  • 不關(guān)心場(chǎng)景,關(guān)心模型完整性和業(yè)務(wù)規(guī)則
  • 不關(guān)心誰(shuí)來(lái),不關(guān)心場(chǎng)景完整的業(yè)務(wù),關(guān)心當(dāng)前上下文的業(yè)務(wù)完整
  • 強(qiáng)一致性事務(wù)放到這層,聚合的事務(wù)是 "理所當(dāng)然的"
  • 對(duì)應(yīng)到分布式系統(tǒng)中的 domain service、后臺(tái)等概念
  • 領(lǐng)域?qū)幼鰳I(yè)務(wù)規(guī)則驗(yàn)證
  • 產(chǎn)生業(yè)務(wù)規(guī)則異常,例如用戶(hù)退款條件不滿(mǎn)足,對(duì)應(yīng)狀態(tài)碼 412、419 等
  • 數(shù)據(jù)權(quán)限放到這層(比如只允許刪除自己創(chuàng)建的商品),因?yàn)閿?shù)據(jù)權(quán)限涉及業(yè)務(wù)規(guī)則
  • 準(zhǔn)單體系統(tǒng)下按照上下文分包,上下文之間調(diào)用必須走領(lǐng)域 domain service,目的就是解耦
  • 上下文中分聚合,聚合根要足夠小,只允許聚合根擁有對(duì)應(yīng)的 domain service
  • 根據(jù)業(yè)務(wù)情況,參考反范式理論,跨上下文使用值對(duì)象做必要的數(shù)據(jù)冗余

基礎(chǔ)設(shè)施層

對(duì)于基礎(chǔ)設(shè)施層來(lái)說(shuō),技術(shù)設(shè)施層并不是指 MySQL、Redis 等外部組件,而是外部組件的適配器,Hibernate、Mybatis、Redis Template 等,因此在 DDD 中適配器模式被多次提到,基礎(chǔ)設(shè)施層往往不能單獨(dú)存在,還是要依附于領(lǐng)域?qū)印<夹g(shù)設(shè)施層的適配器還包括了外部系統(tǒng)的適配,互聯(lián)網(wǎng)產(chǎn)品系統(tǒng)的外部系統(tǒng)非常多,常見(jiàn)的有活體監(jiān)測(cè)、風(fēng)控系統(tǒng)、稅務(wù)發(fā)票等。

技術(shù)設(shè)施層的特點(diǎn):

  • 關(guān)心存儲(chǔ)、通知、第三方系統(tǒng)等外部設(shè)施(防腐層隔離)
  • 如果使用自動(dòng)化的 ORM,這層可以在一定程度上省略
  • 基礎(chǔ)設(shè)施異常,應(yīng)丟出內(nèi)部異常,對(duì)應(yīng)狀態(tài)碼 500
  • 準(zhǔn)單體系統(tǒng)下按照 adapter 分包
  • 基礎(chǔ)設(shè)施的權(quán)限由配置到應(yīng)用的憑證控制,例如數(shù)據(jù)庫(kù)、對(duì)象存儲(chǔ)的憑證,技術(shù)設(shè)施層不涉及用戶(hù)的權(quán)限

DDD 分層的注意事項(xiàng)

DDD 分層架構(gòu)需要認(rèn)識(shí)到一點(diǎn)是,有時(shí)候我們?cè)陧?xiàng)目中找不到每層之間的明顯的界限,那是因?yàn)槲覀兪褂玫目蚣軒臀覀兺瓿赡骋粚印VC 框架,Spring MVC、Jersey 幫我們搞定了接入層的事情,Hibernate、Redis Template 讓我們感覺(jué)不到基礎(chǔ)設(shè)施層。四層模型并不是一個(gè)刻板的教條,應(yīng)該和你選用的框架做出調(diào)整,DDD 的作者也多次強(qiáng)調(diào)這一點(diǎn)。

另外,基礎(chǔ)設(shè)施層和接入層需要注意兩點(diǎn):

  • 接入層指的是服務(wù)端用于適配端側(cè)的部分,而非端側(cè)本身。因?yàn)榻尤雽颖緛?lái)就依賴(lài)應(yīng)用層,沒(méi)有人使用接口在這里做依賴(lài)倒置,所有又被稱(chēng)作主動(dòng)適配。
  • 基礎(chǔ)設(shè)施層指的是適配基礎(chǔ)設(shè)施的部分,而非基礎(chǔ)設(shè)施本身。開(kāi)發(fā)者往往希望數(shù)據(jù)訪問(wèn)的接口有應(yīng)用來(lái)定義,避免和基礎(chǔ)設(shè)施綁定,提供替換的可能,因此這里往往大量使用接口,會(huì)有一些依賴(lài)倒置的實(shí)現(xiàn),所以又被稱(chēng)作為被動(dòng)適配。關(guān)于依賴(lài)倒置的知識(shí),可以了解面向?qū)ο蟮囊恍┗A(chǔ)概念。

DDD 分層到四種架構(gòu)的映射

我們把這四層合到一起部署就是準(zhǔn)單體系統(tǒng),分開(kāi)部署就是微服務(wù)、SOA。

更加有意思的是,在準(zhǔn)單體系統(tǒng)中,如果我們嚴(yán)格限定領(lǐng)域?qū)又心K之間的耦合關(guān)系,應(yīng)用層訪問(wèn)領(lǐng)域?qū)邮峭ㄟ^(guò)本地方法調(diào)用的。當(dāng)我們想改造成微服務(wù)實(shí)現(xiàn)時(shí),只需要簡(jiǎn)單的抽象一個(gè)接口,然后通過(guò)遠(yuǎn)程調(diào)用實(shí)現(xiàn)它,無(wú)論是 RPC、還是 Restful 訪問(wèn)都不是大問(wèn)題。

當(dāng)然我們得解決遠(yuǎn)程調(diào)用后的一系列問(wèn)題,以及領(lǐng)域?qū)邮墙怦盍己玫摹?/p>

準(zhǔn)單體系統(tǒng)

準(zhǔn)單體系統(tǒng)架構(gòu)下,所有的代碼在一個(gè)代碼倉(cāng)庫(kù),四層架構(gòu)依然,往往通過(guò)多模塊組織代碼。應(yīng)用層通過(guò)不同的模塊實(shí)現(xiàn),然后將領(lǐng)域服務(wù)抽出來(lái)一個(gè)公用模塊。很多小型項(xiàng)目依然保持這種形態(tài),每層能保持良好的依賴(lài)關(guān)系非常重要。 每層之間最好依次向下調(diào)用,DDD 的書(shū)中有一個(gè)不好的示例,上層可以跳過(guò)中間層直接調(diào)用下層。

很多內(nèi)網(wǎng)部署的傳統(tǒng)項(xiàng)目單機(jī)就能滿(mǎn)足,小型公司的 OA 軟件、餐飲軟件、會(huì)員管理系統(tǒng)的單機(jī)版就是通過(guò)這種方式部署。

低級(jí)別分布式系統(tǒng)

將應(yīng)用水平拓展,數(shù)據(jù)庫(kù)進(jìn)行主從拆分,Redis 使用主從或哨兵模式,本質(zhì)上和準(zhǔn)單體系統(tǒng)沒(méi)有區(qū)別,應(yīng)用沒(méi)有垂直拓展復(fù)雜性不會(huì)有特別大的提升。

還有一種折中的方式,應(yīng)用層各個(gè)模塊單獨(dú)部署,領(lǐng)域?qū)拥臉I(yè)務(wù)邏輯單獨(dú)部署或者通過(guò) Jar 包的方式加載應(yīng)用中,實(shí)現(xiàn)應(yīng)用層的解耦,并且不會(huì)帶來(lái)分布式的問(wèn)題。

基于上面這種模式的變體,下面這種部署方式也有很多,通過(guò)這種部署方式,領(lǐng)域服務(wù)使用嚴(yán)謹(jǐn)?shù)?Java 實(shí)現(xiàn),接入層和應(yīng)用層使用 PHP、Nodejs 等動(dòng)態(tài)語(yǔ)言實(shí)現(xiàn)。

高級(jí)別分布式系統(tǒng)

如果我們把應(yīng)用和領(lǐng)域?qū)佣吉?dú)立部署,就得到了現(xiàn)在主流的微服務(wù)架構(gòu)。只不過(guò)在微服務(wù)的語(yǔ)境下,應(yīng)用層 + 接入層被稱(chēng)為 BFF (Backend for Frontend),領(lǐng)域?qū)迂?fù)責(zé)實(shí)現(xiàn)業(yè)務(wù)邏輯,應(yīng)用層用于各種業(yè)務(wù)場(chǎng)景下的適配。

然而這種設(shè)計(jì)會(huì)受到一些批評(píng),他們認(rèn)為這不是正宗的微服務(wù),而像現(xiàn)在所說(shuō)的中臺(tái)。部分微服務(wù)的工程師倡導(dǎo)使用 API Gateway 的方式將領(lǐng)域服務(wù)的 API 直接暴露給端側(cè)。

實(shí)際上這種做法應(yīng)用層并沒(méi)有消失,編排領(lǐng)域服務(wù) API 的職責(zé)被下放到端側(cè),在一些特殊的業(yè)務(wù)場(chǎng)景下沒(méi)有問(wèn)題,但是大多數(shù)場(chǎng)景下并不合適。業(yè)務(wù)邏輯容易造成碎片化,存在調(diào)用次數(shù)多,服務(wù)間最終一致性事務(wù)難以實(shí)現(xiàn)等問(wèn)題。下面這張圖表達(dá)了這種設(shè)計(jì)方式,但大多數(shù)情況下并不推薦。

到此,領(lǐng)域?qū)颖淮怪辈鸱郑S之而來(lái)的就是我們熟知的各種分布式問(wèn)題了,熔斷、負(fù)載均衡等問(wèn)題屬于技術(shù)復(fù)雜度可以在業(yè)務(wù)無(wú)感知的情況下被解決,但下面幾個(gè)問(wèn)題需要侵入業(yè)務(wù)才能被良好的解決,因此還需要 DDD 的幫助。

  • 領(lǐng)域?qū)幽K之間的事務(wù)怎么處理?
  • 領(lǐng)域?qū)幽K之間需要表關(guān)聯(lián)怎么辦?
  • 領(lǐng)域?qū)邮菬o(wú)狀態(tài)的,怎么做權(quán)限控制?
  • 領(lǐng)域?qū)幽K之間的依賴(lài)關(guān)系怎么處理?

我們?cè)诤竺娴?《DDD 指導(dǎo)應(yīng)用垂直拆分后的問(wèn)題》部分回答。

復(fù)雜分布式系統(tǒng)

高級(jí)別的分布式系統(tǒng)已經(jīng)是業(yè)界大的互聯(lián)網(wǎng)公司的主流做法,不過(guò)在一些極端復(fù)雜的系統(tǒng)中,依然不能滿(mǎn)足業(yè)務(wù)需要。倒不是技術(shù)上一定要拆的非常細(xì),主要是參與開(kāi)發(fā)的人數(shù)多、代碼量大,團(tuán)隊(duì)協(xié)作、版本構(gòu)建有很多問(wèn)題。

一個(gè)最佳的敏捷團(tuán)隊(duì)為 10 到 15人,除去測(cè)試、業(yè)務(wù)分析師,開(kāi)發(fā)者一般在 10 人左右。因此在非常復(fù)雜的系統(tǒng)中盡可能把能拆分的都拆出去。繼續(xù)拆分往往有兩個(gè)方向:

  1. 變得復(fù)雜的接入層,在應(yīng)用層里面兜不住了。例如 socket 連接相當(dāng)費(fèi)資源,可以剝離出去單獨(dú)建立連接,然后和收銀機(jī)應(yīng)用通信。
  2. 一些外部系統(tǒng)的適配層,例如短信網(wǎng)關(guān)、稅務(wù)系統(tǒng)適配服務(wù)。

某大型 lot 平臺(tái)將對(duì)接端側(cè)的服務(wù)根據(jù)接入?yún)f(xié)議拆分,HTTP、MQTT、XMPP 然后轉(zhuǎn)換數(shù)據(jù)格式后統(tǒng)一送入。不過(guò),這種場(chǎng)景已經(jīng)比較少見(jiàn)。


文/ThoughtWorks 少個(gè)分號(hào)

更多精彩洞見(jiàn),請(qǐng)關(guān)注微信公眾號(hào):ThoughtWorks洞見(jiàn)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,663評(píng)論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,125評(píng)論 3 414
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 175,506評(píng)論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 62,614評(píng)論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,402評(píng)論 6 404
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 54,934評(píng)論 1 321
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,021評(píng)論 3 440
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 42,168評(píng)論 0 287
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,690評(píng)論 1 333
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,596評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,784評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,288評(píng)論 5 357
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,027評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 34,404評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 35,662評(píng)論 1 280
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,398評(píng)論 3 390
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,743評(píng)論 2 370

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