「领域驱动设计」DDD,六边形架构,洋葱架构,整洁架构,CQRS的整合架构
這篇文章是軟件架構(gòu)編年史的一部分,一系列關(guān)于軟件架構(gòu)的文章。在這些文章中,我寫了我對(duì)軟件架構(gòu)的了解,我如何看待它,以及我如何使用這些知識(shí)。如果您閱讀了本系列以前的文章,那么本文的內(nèi)容可能更有意義。
大學(xué)畢業(yè)后,我從事了高中教師的職業(yè),直到幾年前,我決定放棄它,成為一名全職軟件開(kāi)發(fā)人員。
從那以后,我總是覺(jué)得我需要找回失去的時(shí)間,盡可能多地、盡可能快地學(xué)習(xí)。因此,我有點(diǎn)沉迷于試驗(yàn)、閱讀和寫作,特別關(guān)注軟件設(shè)計(jì)和體系結(jié)構(gòu)。這就是我寫這些帖子的原因,來(lái)幫助我學(xué)習(xí)。
在我的上一篇文章中,我寫了很多我學(xué)過(guò)的概念和原則,以及我是如何推理的。但我認(rèn)為這些只是拼圖的一部分。
今天的帖子是關(guān)于我如何將所有這些部分組合在一起的,我似乎應(yīng)該給它起個(gè)名字,我稱它為顯式架構(gòu)(Explicit Architecture)。此外,這些概念都“通過(guò)了它們的考驗(yàn)”,并被用于高要求平臺(tái)上的生產(chǎn)代碼中。一個(gè)是SaaS的e-com平臺(tái),在全球擁有數(shù)千個(gè)網(wǎng)絡(luò)商店,另一個(gè)是市場(chǎng),在兩個(gè)國(guó)家都有一個(gè)消息總線,每個(gè)月處理超過(guò)2000萬(wàn)條消息。
系統(tǒng)的基本模塊
工具
將工具和交付機(jī)制連接到應(yīng)用程序核心
端口
主適配器或驅(qū)動(dòng)適配器
輔助或被驅(qū)動(dòng)適配器
控制反轉(zhuǎn)
應(yīng)用程序的核心組織
域服務(wù)
域模型
應(yīng)用程序?qū)?/p>
領(lǐng)域?qū)?/p>
組件
組件之間共享的數(shù)據(jù)存儲(chǔ)
每個(gè)組件隔離數(shù)據(jù)存儲(chǔ)
解耦的組件
觸發(fā)邏輯在其他組件
從其他組件獲取數(shù)據(jù)
控制流
系統(tǒng)的基本模塊
我首先回顧一下EBI和端口及適配器架構(gòu)。它們都明確區(qū)分了哪些代碼是應(yīng)用程序內(nèi)部的,哪些是外部的,以及哪些用于連接內(nèi)部和外部代碼。
此外,端口和適配器體系結(jié)構(gòu)明確標(biāo)識(shí)了系統(tǒng)中的三個(gè)基本代碼塊:
是什么使得運(yùn)行一個(gè)用戶界面成為可能,不管它是什么類型的用戶界面;
系統(tǒng)業(yè)務(wù)邏輯,或應(yīng)用程序核心,由用戶界面使用,以實(shí)際使事情發(fā)生;
基礎(chǔ)構(gòu)架代碼,它將我們的應(yīng)用核心與數(shù)據(jù)庫(kù)、搜索引擎或第三方api等工具連接起來(lái)。
應(yīng)用程序核心是我們真正應(yīng)該關(guān)心的。是代碼允許我們的代碼做它應(yīng)該做的事情,是我們的應(yīng)用程序。它可能使用多個(gè)用戶界面(漸進(jìn)式web應(yīng)用程序、移動(dòng)應(yīng)用程序、CLI、API等),但是實(shí)際執(zhí)行工作的代碼是相同的,并且位于應(yīng)用程序內(nèi)核中,不管什么UI觸發(fā)它,都應(yīng)該是一樣的。
可以想象,典型的應(yīng)用程序流從用戶界面中的代碼開(kāi)始,通過(guò)應(yīng)用程序核心到基礎(chǔ)設(shè)施代碼,然后返回到應(yīng)用程序核心,最后向用戶界面交付響應(yīng)。
工具
遠(yuǎn)離系統(tǒng)中最重要的代碼(應(yīng)用程序核心),我們擁有應(yīng)用程序使用的工具,例如數(shù)據(jù)庫(kù)引擎、搜索引擎、Web服務(wù)器或CLI控制臺(tái)(盡管后兩個(gè)也是交付機(jī)制)。
雖然將CLI控制臺(tái)與數(shù)據(jù)庫(kù)引擎放在同一個(gè)“bucket”中可能感覺(jué)有些奇怪,盡管它們有不同類型的用途,但它們實(shí)際上是應(yīng)用程序使用的工具。關(guān)鍵的區(qū)別在于,雖然CLI控制臺(tái)和web服務(wù)器用于告訴應(yīng)用程序執(zhí)行某些操作,但是數(shù)據(jù)庫(kù)引擎是由應(yīng)用程序執(zhí)行某些操作的。這是一個(gè)非常相關(guān)的區(qū)別,因?yàn)樗鼘?duì)我們?nèi)绾螛?gòu)建將這些工具與應(yīng)用程序核心連接起來(lái)的代碼有很強(qiáng)的影響。
將工具和傳送機(jī)制連接到應(yīng)用程序核心
將工具連接到應(yīng)用程序核心的代碼單元稱為適配器(端口和適配器體系結(jié)構(gòu))。適配器是那些有效地實(shí)現(xiàn)代碼的適配器,這些代碼將允許業(yè)務(wù)邏輯與特定的工具通信,反之亦然。
告訴我們的應(yīng)用程序做某事的適配器稱為主適配器或驅(qū)動(dòng)適配器,而由我們的應(yīng)用程序告訴我們做某事的適配器稱為輔助適配器或驅(qū)動(dòng)適配器。
端口
然而,這些適配器不是隨機(jī)創(chuàng)建的。創(chuàng)建它們是為了將特定的入口點(diǎn)安裝到應(yīng)用程序核心(一個(gè)端口)。端口只不過(guò)是工具如何使用應(yīng)用程序內(nèi)核或應(yīng)用程序內(nèi)核如何使用它的規(guī)范。在大多數(shù)語(yǔ)言及其最簡(jiǎn)單的形式中,這個(gè)規(guī)范,即端口,將是一個(gè)接口,但它實(shí)際上可能由幾個(gè)接口和dto組成。
需要注意的是,端口(接口)屬于業(yè)務(wù)邏輯內(nèi)部,而適配器屬于業(yè)務(wù)邏輯外部。要使此模式正常工作,最重要的是創(chuàng)建適合應(yīng)用程序核心需求的端口,而不是簡(jiǎn)單地模仿工具api。
主適配器或驅(qū)動(dòng)適配器
主適配器或驅(qū)動(dòng)適配器圍繞一個(gè)端口,并使用它來(lái)告訴應(yīng)用程序核心要做什么。它們將來(lái)自交付機(jī)制的任何東西轉(zhuǎn)換為應(yīng)用程序核心中的方法調(diào)用。
換句話說(shuō),我們的驅(qū)動(dòng)適配器是控制器或控制臺(tái)命令,它們?cè)跇?gòu)造函數(shù)中注入一些對(duì)象,這些對(duì)象的類實(shí)現(xiàn)控制器或控制臺(tái)命令所需的接口(端口)。
在更具體的示例中,端口可以是控制器所需的服務(wù)接口或存儲(chǔ)庫(kù)接口。然后將服務(wù)、存儲(chǔ)庫(kù)或查詢的具體實(shí)現(xiàn)注入并在控制器中使用。
或者,端口可以是命令總線或查詢總線接口。在這種情況下,將命令或查詢總線的具體實(shí)現(xiàn)注入控制器,然后控制器構(gòu)造命令或查詢并將其傳遞給相關(guān)總線。
輔助或被驅(qū)動(dòng)適配器
與圍繞端口的被驅(qū)動(dòng)適配器不同,驅(qū)動(dòng)適配器實(shí)現(xiàn)一個(gè)端口和一個(gè)接口,然后將其注入到應(yīng)用程序核心中,無(wú)論哪里需要端口(類型暗示)。
例如,假設(shè)我們有一個(gè)需要持久化數(shù)據(jù)的簡(jiǎn)單應(yīng)用程序。所以我們創(chuàng)建一個(gè)持久性接口,滿足其需要,用一個(gè)方法來(lái)保存數(shù)組的數(shù)據(jù)和方法來(lái)刪除表中的一行的ID。從那時(shí)起,無(wú)論應(yīng)用程序需要保存或刪除數(shù)據(jù),我們需要在其構(gòu)造函數(shù)實(shí)現(xiàn)持久化的對(duì)象我們定義的接口。
現(xiàn)在我們創(chuàng)建一個(gè)特定于MySQL的適配器來(lái)實(shí)現(xiàn)這個(gè)接口。它將具有保存數(shù)組和刪除表中的一行的方法,并且我們將在需要持久性接口的地方注入它。
如果在某個(gè)時(shí)候我們決定改變數(shù)據(jù)庫(kù)供應(yīng)商,比如PostgreSQL或MongoDB,我們只需要?jiǎng)?chuàng)建一個(gè)新的適配器來(lái)實(shí)現(xiàn)PostgreSQL特定的持久化接口,并注入新的適配器而不是舊的。
控制反轉(zhuǎn)
關(guān)于此模式需要注意的一個(gè)特征是,適配器依賴于特定的工具和特定的端口(通過(guò)實(shí)現(xiàn)接口)。但是我們的業(yè)務(wù)邏輯只依賴于端口(接口),它被設(shè)計(jì)成適合業(yè)務(wù)邏輯需求,所以它不依賴于特定的適配器或工具。
這意味著依賴的方向是朝向中心的,這是建筑層面的控制原則的倒置。
盡管如此,創(chuàng)建端口是為了滿足應(yīng)用程序的核心需求,而不是簡(jiǎn)單地模仿工具api,這一點(diǎn)非常重要。
應(yīng)用程序的核心組織
Onion架構(gòu)采用DDD層,并將它們合并到端口和適配器架構(gòu)中。這些層旨在為業(yè)務(wù)邏輯、端口和適配器的內(nèi)部“六邊形”帶來(lái)一些組織,就像端口和適配器一樣,依賴關(guān)系的方向是向中心的。
應(yīng)用程序?qū)?/h2>
用例是可以由應(yīng)用程序中的一個(gè)或多個(gè)用戶接口在應(yīng)用程序核心中觸發(fā)的流程。例如,在CMS中,我們可以有普通用戶使用的實(shí)際應(yīng)用程序UI、CMS管理員使用的另一個(gè)獨(dú)立UI、另一個(gè)CLI UI和web API。這些ui(應(yīng)用程序)可以觸發(fā)特定于其中一個(gè)或由其中幾個(gè)重用的用例。
用例在應(yīng)用層中定義,這是DDD提供的第一層,由Onion Architecture使用。
這一層包含作為第一類公民的應(yīng)用程序服務(wù)(及其接口),但它也包含端口和適配器接口(端口),其中包括ORM接口、搜索引擎接口、消息傳遞接口等等。在我們使用命令總線和/或查詢總線的情況下,這一層是命令和查詢各自的處理程序所在的地方。
應(yīng)用程序服務(wù)和/或命令處理程序包含展開(kāi)用例(業(yè)務(wù)流程)的邏輯。一般來(lái)說(shuō),他們的職責(zé)是:
使用存儲(chǔ)庫(kù)查找一個(gè)或多個(gè)實(shí)體;
告訴那些實(shí)體去做一些域邏輯;
并使用存儲(chǔ)庫(kù)再次持久化實(shí)體,有效地保存數(shù)據(jù)更改。
命令處理程序可以用兩種不同的方式使用:
它們可以包含執(zhí)行用例的實(shí)際邏輯;
它們可以在我們的體系結(jié)構(gòu)中用作簡(jiǎn)單的連接塊,接收命令并簡(jiǎn)單地觸發(fā)存在于應(yīng)用程序服務(wù)中的邏輯。
使用哪種方法取決于上下文,例如:
我們是否已經(jīng)準(zhǔn)備好了應(yīng)用程序服務(wù)并正在添加命令總線?
命令總線是否允許指定任何類/方法作為處理程序,或者它們是否需要擴(kuò)展或?qū)崿F(xiàn)現(xiàn)有的類或接口?
這一層還包含應(yīng)用程序事件的觸發(fā),這些事件表示用例的一些結(jié)果。這些事件觸發(fā)的邏輯是用例的副作用,比如發(fā)送電子郵件、通知第三方API、發(fā)送推送通知,甚至啟動(dòng)屬于應(yīng)用程序不同組件的另一個(gè)用例。
領(lǐng)域?qū)?/h2>
再往里,我們有域?qū)?。這個(gè)層中的對(duì)象包含數(shù)據(jù)和操作數(shù)據(jù)的邏輯,這是特定于域本身的,它獨(dú)立于觸發(fā)邏輯的業(yè)務(wù)流程,它們是獨(dú)立的,完全不知道應(yīng)用層。
域服務(wù)
如前所述,應(yīng)用服務(wù)的作用是:
使用存儲(chǔ)庫(kù)查找一個(gè)或多個(gè)實(shí)體;
告訴那些實(shí)體去做一些域邏輯;
并使用存儲(chǔ)庫(kù)再次持久化實(shí)體,有效地保存數(shù)據(jù)更改。
然而,有時(shí)我們會(huì)遇到一些涉及不同實(shí)體的域邏輯,不管它們是否屬于同一類型,我們覺(jué)得域邏輯不屬于實(shí)體本身,我們覺(jué)得那個(gè)邏輯不是它們的直接責(zé)任。
因此,我們的第一反應(yīng)可能是將邏輯放在實(shí)體之外的應(yīng)用程序服務(wù)中。然而,這意味著該域邏輯將不能在其他用例中重用:域邏輯應(yīng)該遠(yuǎn)離應(yīng)用程序?qū)?
解決方案是創(chuàng)建一個(gè)域服務(wù),它的角色是接收一組實(shí)體并在其上執(zhí)行一些業(yè)務(wù)邏輯。域服務(wù)屬于域?qū)?#xff0c;因此它對(duì)應(yīng)用層中的類一無(wú)所知,比如應(yīng)用程序服務(wù)或存儲(chǔ)庫(kù)。另一方面,它可以使用其他域服務(wù),當(dāng)然還有域模型對(duì)象。
域模型
在最中心的是域模型,它不依賴于它之外的任何東西,它包含表示域內(nèi)某些內(nèi)容的業(yè)務(wù)對(duì)象。這些對(duì)象的示例首先是實(shí)體,但也包括值對(duì)象、枚舉和域模型中使用的任何對(duì)象。
域模型也是域事件“活動(dòng)”的地方。當(dāng)特定的一組數(shù)據(jù)發(fā)生更改時(shí),將觸發(fā)這些事件,并將這些更改隨身攜帶。換句話說(shuō),當(dāng)一個(gè)實(shí)體發(fā)生更改時(shí),將觸發(fā)一個(gè)域事件,它將攜帶更改后的屬性新值。例如,這些事件非常適合用于事件源。
組件
到目前為止,我們一直在基于層隔離代碼,但這是細(xì)粒度的代碼隔離。粗粒度的代碼隔離至少是同樣重要的,它是根據(jù)子域和有界上下文來(lái)隔離代碼的,遵循Robert C. Martin在尖叫聲架構(gòu)中表達(dá)的思想。這通常被稱為“按功能包”或“按組件包”,而不是“按層包”,Simon Brown在他的博客“按組件包和體系結(jié)構(gòu)對(duì)齊測(cè)試”中對(duì)此做了很好的解釋:
我是“按組件打包”方法的倡導(dǎo)者,并且根據(jù)Simon Brown關(guān)于按組件打包的圖表,我將無(wú)恥地將其更改為以下內(nèi)容:
這些代碼部分與前面描述的層是交叉的,它們是我們的應(yīng)用程序的組件。組件的示例可以是身份驗(yàn)證、授權(quán)、計(jì)費(fèi)、用戶、審查或帳戶,但它們始終與域相關(guān)。像授權(quán)和/或身份驗(yàn)證這樣的有界上下文應(yīng)該被視為外部工具,我們?yōu)槠鋭?chuàng)建適配器并隱藏在某種端口之后。
解耦的組件
就像細(xì)粒度的代碼單元(類、接口、特征、混合等)一樣,粗粒度的代碼單元(組件)也受益于低耦合和高內(nèi)聚。
為了解耦類,我們使用依賴注入,將依賴注入到類中而不是在類中實(shí)例化,依賴倒置,使類依賴于抽象(接口和/或抽象類)而不是具體類。這意味著子類不知道它將要使用的具體類,它沒(méi)有引用它所依賴的類的完全限定類名。
同樣,完全解耦的組件意味著一個(gè)組件不直接知道任何其他組件。換句話說(shuō),它沒(méi)有引用來(lái)自另一個(gè)組件的任何細(xì)粒度代碼單元,甚至沒(méi)有接口!這意味著依賴注入和依賴倒置不足以解耦組件,我們需要某種架構(gòu)結(jié)構(gòu)。我們可能需要事件、共享內(nèi)核、最終一致性,甚至發(fā)現(xiàn)服務(wù)!
在其他組件觸發(fā)邏輯
當(dāng)我們的一個(gè)組件(組件B)需要在另一個(gè)組件(組件A)中發(fā)生其他事情時(shí)執(zhí)行某個(gè)操作時(shí),我們不能簡(jiǎn)單地從組件A直接調(diào)用組件B中的類/方法,因?yàn)檫@樣A就會(huì)被耦合到B。
然而,我們可以使用事件分派器來(lái)分派一個(gè)應(yīng)用程序事件,該應(yīng)用程序事件將被交付給監(jiān)聽(tīng)它的任何組件,包括B,而B中的事件偵聽(tīng)器將觸發(fā)所需的操作。這意味著組件A將依賴于事件分配器,但它將與B解耦。
然而,如果事件本身“存在”于A中,這意味著B知道A的存在,它與A是耦合的。這意味著組件都依賴于共享內(nèi)核,但是它們之間是解耦的。共享內(nèi)核將包含應(yīng)用程序和域事件之類的功能,但它也可以包含規(guī)范對(duì)象,以及任何需要共享的內(nèi)容,請(qǐng)記住,共享內(nèi)核的任何更改都將影響到應(yīng)用程序的所有組件,因此共享內(nèi)核應(yīng)該盡可能少。此外,如果我們有一個(gè)多語(yǔ)言系統(tǒng),假設(shè)是一個(gè)微服務(wù)生態(tài)系統(tǒng),其中它們是用不同的語(yǔ)言編寫的,那么共享內(nèi)核需要是語(yǔ)言無(wú)關(guān)的,以便所有組件都可以理解它,無(wú)論它們是用什么語(yǔ)言編寫的。例如,它將包含事件描述,而不是包含事件類的共享內(nèi)核。名稱、屬性、甚至方法(盡管這些在JSON之類的不可知語(yǔ)言中可能更有用),這樣所有組件/微服務(wù)都可以解釋它,甚至自動(dòng)生成它們自己的具體實(shí)現(xiàn)。請(qǐng)?jiān)谖业暮罄m(xù)文章中相關(guān)內(nèi)容:不僅僅是同心圓層。
這種方法既適用于單片應(yīng)用程序,也適用于像微服務(wù)生態(tài)系統(tǒng)這樣的分布式應(yīng)用程序。然而,當(dāng)事件只能異步交付時(shí),對(duì)于需要立即在其他組件中執(zhí)行觸發(fā)邏輯的上下文,這種方法是不夠的!組件將需要一個(gè)直接的HTTP調(diào)用組件b。在這種情況下,解耦的組件,我們需要發(fā)現(xiàn)服務(wù),將要求它應(yīng)該發(fā)送請(qǐng)求來(lái)啟動(dòng)所需的行動(dòng),或者使請(qǐng)求發(fā)現(xiàn)服務(wù)代理的相關(guān)服務(wù),最終將響應(yīng)返回給請(qǐng)求者。此方法將把組件耦合到發(fā)現(xiàn)服務(wù),但將使它們彼此解耦。
從其他組件獲取數(shù)據(jù)
在我看來(lái),一個(gè)組件不允許改變它不“擁有”的數(shù)據(jù),但是它可以查詢和使用任何數(shù)據(jù)。
組件之間共享的數(shù)據(jù)存儲(chǔ)
當(dāng)一個(gè)組件需要使用屬于另一個(gè)組件的數(shù)據(jù)時(shí),假設(shè)一個(gè)賬單組件需要使用屬于accounts組件的客戶端名稱,賬單組件將包含一個(gè)查詢對(duì)象,該對(duì)象將查詢?cè)摂?shù)據(jù)的數(shù)據(jù)存儲(chǔ)。這僅僅意味著賬單組件可以知道任何數(shù)據(jù)集,但是它必須通過(guò)查詢的方式將不“擁有”的數(shù)據(jù)作為只讀數(shù)據(jù)使用。
每個(gè)組件隔離數(shù)據(jù)存儲(chǔ)
在本例中,應(yīng)用了相同的模式,但是我們?cè)跀?shù)據(jù)存儲(chǔ)級(jí)別上更加復(fù)雜。組件擁有自己的數(shù)據(jù)存儲(chǔ)意味著每個(gè)數(shù)據(jù)存儲(chǔ)包含:
它擁有的一組數(shù)據(jù),并且是唯一允許更改的數(shù)據(jù),使其成為唯一的真理來(lái)源;
一組數(shù)據(jù)是其他組件數(shù)據(jù)的副本,它不能自己更改這些數(shù)據(jù),但是組件功能需要它,并且需要在所有者組件中發(fā)生更改時(shí)對(duì)其進(jìn)行更新。
每個(gè)組件將從其他組件創(chuàng)建所需數(shù)據(jù)的本地副本,以便在需要時(shí)使用。當(dāng)擁有該組件的組件中的數(shù)據(jù)發(fā)生更改時(shí),該所有者組件將觸發(fā)承載數(shù)據(jù)更改的域事件。持有該數(shù)據(jù)副本的組件將偵聽(tīng)該域事件,并相應(yīng)地更新其本地副本。
控制流
正如我上面所說(shuō)的,控制流當(dāng)然是從用戶到應(yīng)用程序核心,再到基礎(chǔ)設(shè)施工具,最后回到應(yīng)用程序核心,最后回到用戶。但是類到底是如何組合在一起的呢?哪些取決于哪些?我們?nèi)绾谓M合它們?
在Bob叔叔關(guān)于干凈架構(gòu)的文章中,我將嘗試用UMLish圖來(lái)解釋控制流……
沒(méi)有命令/查詢總線
在我們不使用命令總線的情況下,控制器將依賴于應(yīng)用程序服務(wù)或查詢對(duì)象。
[編輯- 2017-11-18]我完全錯(cuò)過(guò)了我用來(lái)從查詢返回?cái)?shù)據(jù)的DTO,所以我現(xiàn)在添加了它。感謝MorphineAdministered公司為我指出了這一點(diǎn)。
在上面的圖中我們使用應(yīng)用程序的接口服務(wù),盡管我們可能認(rèn)為這不是真正需要從應(yīng)用程序服務(wù)是我們應(yīng)用程序代碼的一部分,我們不會(huì)想交換另一個(gè)實(shí)現(xiàn),盡管我們可能完全重構(gòu)它。
查詢對(duì)象將包含一個(gè)優(yōu)化的查詢,該查詢將簡(jiǎn)單地返回一些原始數(shù)據(jù)以顯示給用戶。該數(shù)據(jù)將以DTO的形式返回,并注入到ViewModel中。這個(gè)視圖模型可能有一些視圖邏輯,它將被用來(lái)填充一個(gè)視圖。
另一方面,應(yīng)用程序服務(wù)將包含用例邏輯,當(dāng)我們希望在系統(tǒng)中執(zhí)行某些操作時(shí),而不是簡(jiǎn)單地查看某些數(shù)據(jù)時(shí),將觸發(fā)該邏輯。應(yīng)用程序服務(wù)依賴于存儲(chǔ)庫(kù),存儲(chǔ)庫(kù)將返回包含需要觸發(fā)的邏輯的實(shí)體。它還可能依賴于域服務(wù)來(lái)協(xié)調(diào)多個(gè)實(shí)體中的域流程,但情況并非如此。
在展開(kāi)用例之后,應(yīng)用程序服務(wù)可能希望通知整個(gè)系統(tǒng)該用例已經(jīng)發(fā)生,在這種情況下,它還將依賴于事件分派器來(lái)觸發(fā)事件。
值得注意的是,我們?cè)诔志眯砸婧痛鎯?chǔ)庫(kù)上都放置了接口。雖然看起來(lái)有些多余,但它們有不同的用途:
持久性接口是ORM上的一個(gè)抽象層,因此我們可以交換正在使用的ORM,而不需要更改應(yīng)用程序的核心。
repository接口是對(duì)持久性引擎本身的抽象。假設(shè)我們想從MySQL切換到MongoDB。持久性接口可以是相同的,如果我們想繼續(xù)使用相同的ORM,那么即使是持久性適配器也可以保持不變。但是,查詢語(yǔ)言是完全不同的,所以我們可以創(chuàng)建使用相同持久性機(jī)制的新存儲(chǔ)庫(kù),實(shí)現(xiàn)相同的存儲(chǔ)庫(kù)接口,但是使用MongoDB查詢語(yǔ)言而不是SQL構(gòu)建查詢。
使用命令/查詢總線
在我們的應(yīng)用程序使用命令/查詢總線的情況下,除了控制器現(xiàn)在依賴于總線和命令或查詢外,關(guān)系圖幾乎保持不變。它將實(shí)例化命令或查詢,并將其傳遞給總線,總線將找到適當(dāng)?shù)奶幚沓绦騺?lái)接收和處理命令。
在下面的關(guān)系圖中,命令處理程序然后使用應(yīng)用程序服務(wù)。然而,這并不總是需要的,事實(shí)上在大多數(shù)情況下,處理程序?qū)美乃羞壿?。如果需要在另一個(gè)處理程序中重用相同的邏輯,則只需要將邏輯從處理程序提取到單獨(dú)的應(yīng)用程序服務(wù)中。
[編輯- 2017-11-18]我完全錯(cuò)過(guò)了我用來(lái)從查詢返回?cái)?shù)據(jù)的DTO,所以我現(xiàn)在添加了它。感謝MorphineAdministered公司為我指出了這一點(diǎn)。
您可能已經(jīng)注意到,總線與命令、查詢和處理程序之間沒(méi)有依賴關(guān)系。這是因?yàn)?#xff0c;為了提供良好的解耦,它們實(shí)際上應(yīng)該彼此不了解??偩€知道什么處理程序應(yīng)該處理什么命令或查詢的方式應(yīng)該通過(guò)簡(jiǎn)單的配置來(lái)設(shè)置。
如您所見(jiàn),在這兩種情況下,跨越應(yīng)用程序核心邊界的所有箭頭和依賴項(xiàng)都指向內(nèi)部。如前所述,這是端口和適配器體系結(jié)構(gòu)、Onion體系結(jié)構(gòu)和Clean體系結(jié)構(gòu)的基本規(guī)則。
結(jié)論
一如既往,我們的目標(biāo)是擁有一個(gè)松散耦合和高內(nèi)聚的代碼庫(kù),這樣修改起來(lái)就容易、快速和安全。
計(jì)劃是沒(méi)有價(jià)值的,但計(jì)劃就是一切。 -------------------------------艾森豪威爾這個(gè)信息圖是一個(gè)概念圖。了解和理解所有這些概念將幫助我們規(guī)劃一個(gè)健康的架構(gòu),一個(gè)健康的應(yīng)用程序。
然而:
地圖不是領(lǐng)土。 -----------------------阿爾弗雷德Korzybski這意味著這些只是指導(dǎo)方針!應(yīng)用程序是我們需要應(yīng)用知識(shí)的領(lǐng)域、現(xiàn)實(shí)和具體用例,這就是定義實(shí)際體系結(jié)構(gòu)的內(nèi)容!
我們需要理解所有這些模式,但是為了解耦和內(nèi)聚,我們還需要思考并準(zhǔn)確地理解我們的應(yīng)用程序需要什么,我們應(yīng)該走多遠(yuǎn)。這個(gè)決策可以依賴于許多因素,從項(xiàng)目功能需求開(kāi)始,但是也可以包括諸如構(gòu)建應(yīng)用程序的時(shí)間框架、應(yīng)用程序的生命周期、開(kāi)發(fā)團(tuán)隊(duì)的經(jīng)驗(yàn)等因素。
就是這樣,這就是我理解這一切的方式。這就是我在腦海里給它找的合理解釋。
我在后續(xù)的文章中進(jìn)一步擴(kuò)展了這些想法:不僅僅是同心圓層。
但是,我們?nèi)绾卧诖a庫(kù)中顯式地實(shí)現(xiàn)這一切呢?這是我下一篇文章的主題:如何在代碼中反映體系結(jié)構(gòu)和域。
| 本文 | http://jiagoushi.pro/ddd-hexagonal-onion-clean-cqrs-how-i-put-it-all-together | |
| 討論:請(qǐng)加入知識(shí)星球【首席架構(gòu)師圈】或者加微信小號(hào)【jiagoushi_pro】或者加QQ群【11107777】 | ||
| 公眾號(hào) | 【jiagoushipro】 【超級(jí)架構(gòu)師】 精彩圖文詳解架構(gòu)方法論,架構(gòu)實(shí)踐,技術(shù)原理,技術(shù)趨勢(shì)。 我們?cè)诘饶?#xff0c;趕快掃描關(guān)注吧。 | |
| 微信小號(hào) | 【cea_csa_cto】 50000人社區(qū),討論:企業(yè)架構(gòu),云計(jì)算,大數(shù)據(jù),數(shù)據(jù)科學(xué),物聯(lián)網(wǎng),人工智能,安全,全棧開(kāi)發(fā),DevOps,數(shù)字化. | |
| QQ群 | 【11107767】深度交流企業(yè)架構(gòu),業(yè)務(wù)架構(gòu),應(yīng)用架構(gòu),數(shù)據(jù)架構(gòu),技術(shù)架構(gòu),集成架構(gòu),安全架構(gòu)。以及大數(shù)據(jù),云計(jì)算,物聯(lián)網(wǎng),人工智能等各種新興技術(shù)。 加QQ群,有珍貴的報(bào)告和干貨資料分享。 | |
| 視頻號(hào) | 【超級(jí)架構(gòu)師】 1分鐘快速了解架構(gòu)相關(guān)的基本概念,模型,方法,經(jīng)驗(yàn)。 每天1分鐘,架構(gòu)心中熟。 | |
| 知識(shí)星球 | 向大咖提問(wèn),近距離接觸,或者獲得私密資料分享。 | 知識(shí)星球【首席架構(gòu)師圈】 |
| 微信圈子 | 志趣相投的同好交流。 | 微信圈子【首席架構(gòu)師圈】 |
| 喜馬拉雅 | 路上或者車上了解最新黑科技資訊,架構(gòu)心得。 | 【智能時(shí)刻,架構(gòu)君和你聊黑科技】 |
| 知識(shí)星球 | 認(rèn)識(shí)更多朋友,職場(chǎng)和技術(shù)閑聊。 | 知識(shí)星球【職場(chǎng)和技術(shù)】 |
| 微博 | 【智能時(shí)刻】 | 智能時(shí)刻 |
| 嗶哩嗶哩 | 【超級(jí)架構(gòu)師】 | |
| 抖音 | 【cea_csa_cto】超級(jí)架構(gòu)師 | |
| 快手 | 【cea_csa_cto】超級(jí)架構(gòu)師 | |
| 小紅書 | 【cea_csa_cto】超級(jí)架構(gòu)師 | 首席架構(gòu)師智庫(kù) |
謝謝大家關(guān)注,轉(zhuǎn)發(fā),點(diǎn)贊和點(diǎn)在看。
總結(jié)
以上是生活随笔為你收集整理的「领域驱动设计」DDD,六边形架构,洋葱架构,整洁架构,CQRS的整合架构的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 梁山伯自白书
- 下一篇: Springboot 服务端为App集成