DDD Layers & Clean Architecture DDD分層和簡(jiǎn)潔架構(gòu)
https://blog.csdn.net/u011385940/article/details/122517580
There are four fundamental layers of a Domain Driven Based Solution;
一個(gè)基于領(lǐng)域驅(qū)動(dòng)的解決方案有四層,如下圖所示:
Business Logic places into two layers, the Domain layer and the Application Layer, while they contain different kinds of business logic;
業(yè)務(wù)邏輯分為兩層步清,即領(lǐng)域?qū)雍蛻?yīng)用層要门,然而它們包含不同種類的業(yè)務(wù)邏輯。
Domain Layer implements the core, use-case independent business logic of the domain/system.
領(lǐng)域?qū)?/strong>實(shí)現(xiàn)核心廓啊,用例獨(dú)立于領(lǐng)域/系統(tǒng)的的業(yè)務(wù)邏輯欢搜。
Application Layer implements the use cases of the application based on the domain. A use case can be thought as a user interaction on the User Interface (UI).
應(yīng)用層實(shí)現(xiàn)基于領(lǐng)域的應(yīng)用程序的用例。一個(gè)用例可以被認(rèn)為是用戶在用戶界面(UI)上的交互谴轮。
Presentation Layer contains the UI elements (pages,components) of the application.
表現(xiàn)層包含應(yīng)用程序的用戶界面元素(頁面炒瘟、組件)。
Infrastructure Layer supports other layer by implementing the abstractions and integrations to 3rd-party library and systems.
基礎(chǔ)設(shè)施層通過實(shí)現(xiàn)抽象第步、集成第三方庫和其它系統(tǒng)來支持其他層唧领。
The same layering can be shown as the diagram below and known as the Clean Architecture, or sometimes the Onion Architecture:
同樣的分層被稱為簡(jiǎn)潔架構(gòu),有時(shí)被稱為洋蔥架構(gòu)雌续,如下圖所示:
In the Clean Architecture, each layer only depends on the layer directly inside it. The most independent layer is shown in the most inner circle and it is the Domain Layer.
在簡(jiǎn)潔架構(gòu)中斩个,每一層僅直接依賴于其內(nèi)部的層。最獨(dú)立的一層顯示在最內(nèi)圈驯杜,它就是領(lǐng)域?qū)印?/p>
好的應(yīng)用架構(gòu)受啥,都遵循一些共同模式,不管是六邊形架構(gòu)鸽心、洋蔥圈架構(gòu)滚局、整潔架構(gòu)、還是COLA架構(gòu)顽频,都提倡以業(yè)務(wù)為核心藤肢,解耦外部依賴,分離業(yè)務(wù)復(fù)雜度和技術(shù)復(fù)雜度糯景。
分層架構(gòu)(Layered Architecture)
分層架構(gòu)就是將業(yè)務(wù)應(yīng)用劃分為對(duì)應(yīng)的層級(jí)模塊嘁圈。每個(gè)層職責(zé)不同省骂。
四層結(jié)構(gòu)定義:
接口層: 統(tǒng)一處理系統(tǒng)對(duì)外的服務(wù)接口,可以是直接查詢最住,也可以是三方系統(tǒng)對(duì)接钞澳。
應(yīng)用層: 調(diào)用各個(gè)領(lǐng)域完成一個(gè)具體的業(yè)務(wù)流程應(yīng)用,不需要關(guān)心具體的業(yè)務(wù)實(shí)現(xiàn)涨缚。
領(lǐng)域?qū)樱?實(shí)際完成業(yè)務(wù)邏輯處理的地方轧粟,包含領(lǐng)域模型和領(lǐng)域服務(wù),無須關(guān)心顯示及存儲(chǔ)等相關(guān)問題脓魏。
基礎(chǔ)設(shè)施層: 提供底層支撐功能兰吟,包括持久化、序列化與反序列化茂翔、消息中間件等混蔼。
優(yōu)點(diǎn):
可降低層級(jí)之間依賴;
利于層級(jí)模塊復(fù)用;
安全性高檩电,外部系統(tǒng)只從接口層訪問,統(tǒng)一入口點(diǎn)府树;
項(xiàng)目結(jié)構(gòu)清晰俐末,簡(jiǎn)單。
常見兩種分層架構(gòu)圖:
圖一:四層
圖二:五層
六邊形架構(gòu)(Hexagonal Architecture)
六邊形架構(gòu)可以理解為“去結(jié)構(gòu)化”的架構(gòu)奄侠,系統(tǒng)內(nèi)部與外界的交互過程通過適配器來完成卓箫,沒有層次,所有交互雙方只知道適配器的存在垄潮,不用知曉對(duì)方的存在烹卒。
六邊形又稱為端口適配器模式。六邊形架構(gòu)將系統(tǒng)分為內(nèi)部和外部弯洗,內(nèi)部代表具體的業(yè)務(wù)邏輯旅急,也就是DDD中強(qiáng)調(diào)的領(lǐng)域模型(其中包含領(lǐng)域服務(wù),對(duì)業(yè)務(wù)概念建立的模型等)牡整;外部代表基礎(chǔ)設(shè)施和應(yīng)用等藐吮,類似RESTful API, SOAP, AMQP, 或者數(shù)據(jù)庫,內(nèi)存逃贝,文件系統(tǒng)谣辞,以及自動(dòng)化測(cè)試。 內(nèi)部和外部之間通過端口通信沐扳。端口代表協(xié)議泥从,通常是以API呈現(xiàn)。端口的具體實(shí)現(xiàn)是適配器沪摄,負(fù)責(zé)對(duì)接具體的外部系統(tǒng)或內(nèi)部邏輯躯嫉。該架構(gòu)可以非常容易的實(shí)現(xiàn)外部替換纱烘、依賴倒置、自動(dòng)測(cè)試等功能和敬。
一個(gè)端口對(duì)應(yīng)多個(gè)適配器凹炸,是對(duì)一類外部系統(tǒng)的歸納。
適配器分為主適配器和次適配器:
主適配器(Driving Adapter):代表接收用戶輸入昼弟,調(diào)用端口并返回?cái)?shù)據(jù)啤它。
次適配器(Driven Adapter):實(shí)現(xiàn)應(yīng)用的出口端口,向外部工具執(zhí)行操作舱痘。
六邊形架構(gòu)優(yōu)點(diǎn):
業(yè)務(wù)領(lǐng)域的邊界更加清晰变骡。
更好的可擴(kuò)展性。(比如需要新增一種協(xié)議(數(shù)據(jù)庫芭逝、MQ...)的支持塌碌,那么只需要定義一組端口-適配器即可。對(duì)原有端口-適配器不影響)
對(duì)測(cè)試的友好支持旬盯。
更容易實(shí)施DDD台妆。
整潔架構(gòu)(Clean Architecture)
整潔架構(gòu)(又名洋蔥架構(gòu) Onion Architecture),六邊形架構(gòu)的變種胖翰。
整潔架構(gòu)是Robot C.Martin在《整潔架構(gòu)之道》一書中提出來的架構(gòu)設(shè)計(jì)思想厚宰。它以圓環(huán)的形式把系統(tǒng)分成了幾個(gè)不同的層次江耀,因此又被稱為洋蔥架構(gòu)溢十。
在整潔架構(gòu)里巨税,同心圓代表應(yīng)用軟件的不同部分,從里到外依次是領(lǐng)域模型培他、領(lǐng)域服務(wù)鹃两、應(yīng)用服務(wù)、最外圍是容易變化的內(nèi)容舀凛,如界面和基礎(chǔ)設(shè)施(如數(shù)據(jù)存儲(chǔ)等)俊扳。整潔架構(gòu)是以領(lǐng)域?yàn)橹行模皇且詳?shù)據(jù)為中心猛遍。
整潔架構(gòu)最主要原則是依賴原則拣度,它定義了各層的依賴關(guān)系,越往里螃壤,依賴越低抗果,代碼級(jí)別越高。外圓代碼依賴只能指向內(nèi)圓奸晴,內(nèi)圓不知道外圓的任何事情冤馏。一般來說,外圓的聲明(包括方法寄啼、類逮光、變量)不能被內(nèi)圓引用代箭。同樣的,外圓使用的數(shù)據(jù)格式也不能被內(nèi)圓使用涕刚。
各層職責(zé):
Domain Model:業(yè)務(wù)模型嗡综,對(duì)應(yīng)DDD中的Entity、值對(duì)象等杜漠。
Domain Services:核心業(yè)務(wù)邏輯极景。
Application Services:應(yīng)用的輸入輸出層。
User Interface/Tests/Infrastructure:適配器層(例如數(shù)據(jù)庫驾茴、用戶界面及外部服務(wù))盼樟。
優(yōu)點(diǎn):
各層職責(zé)清晰,提高了大型復(fù)雜項(xiàng)目的可維護(hù)性锈至。
結(jié)合DDD晨缴,使項(xiàng)目以領(lǐng)域模型為主。
保證內(nèi)部核心領(lǐng)域的獨(dú)立和無依賴峡捡,外部技術(shù)細(xì)節(jié)可以通過接口和適配器隨時(shí)更換击碗,在不丟失任何業(yè)務(wù)邏輯的情況下替換掉整個(gè)技術(shù)實(shí)現(xiàn),從而增加系統(tǒng)的靈活性和可測(cè)性们拙。
DDD架構(gòu)(Domain Driven Design Architecture)
準(zhǔn)確地說稍途,DDD不是架構(gòu),而是一種開發(fā)思想睛竣。 DDD帶來的最大改變是讓我們得以從 “數(shù)據(jù)驅(qū)動(dòng)”轉(zhuǎn)向“領(lǐng)域驅(qū)動(dòng)”晰房,讓我們知道領(lǐng)域是應(yīng)用的核心求摇,其它都是技術(shù)細(xì)節(jié)射沟,隨時(shí)可以被替換。
DDD四層架構(gòu)見分層架構(gòu)圖一与境。
DDD采用統(tǒng)一語言验夯,避免組件劃分過程中的邊界錯(cuò)位。 讓業(yè)務(wù)架構(gòu)和系統(tǒng)架構(gòu)形成綁定關(guān)系摔刁,從而建立針對(duì)業(yè)務(wù)變化的高響應(yīng)力架構(gòu)挥转。
戰(zhàn)略層面:針對(duì)業(yè)務(wù)問題分析和分解,通過識(shí)別核心問題域來降低分析的復(fù)雜度共屈。
戰(zhàn)術(shù)層面:識(shí)別問題域里的不同業(yè)務(wù)上下文來進(jìn)行面向業(yè)務(wù)需求的組件化绑谣。
DDD基本概念:
通用語言:通過畫領(lǐng)域模型圖、類圖來建立一種溝通關(guān)系拗引。
實(shí)體 Entity:從屬于某個(gè)聚合根借宵。實(shí)體具有id,有生命周期矾削。
值對(duì)象 Value Object:無生命周期壤玫,歸屬具體的實(shí)體豁护。
聚合 Aggregate:領(lǐng)域模型最底層的邊界。邏輯邊界欲间。一個(gè)聚合只有一個(gè)聚合根楚里。
聚合根 Aggregate Root:最抽象、最普遍的特征猎贴。也叫做根實(shí)體班缎。
工廠 Factory:隱藏對(duì)象的復(fù)雜創(chuàng)建邏輯。
倉庫 Repository:封裝獲取對(duì)象的邏輯嘱能。
限界上下文 Bounded Context:定義了每個(gè)模型的應(yīng)用范圍吝梅,可理解為一個(gè)子域?qū)?yīng)一個(gè)上下文。一個(gè)上下文可能包含多個(gè)聚合惹骂,每個(gè)聚合都有一個(gè)根實(shí)體苏携,叫做聚合根。
COLA架構(gòu) (Clean Object-oriented & Layered Architecture)
一方面COLA是一種架構(gòu)思想对粪,是整合了洋蔥圈架構(gòu)右冻、適配器架構(gòu)、DDD著拭、整潔架構(gòu)纱扭、TMF等架構(gòu)思想的一種應(yīng)用架構(gòu)。
基于擴(kuò)展點(diǎn)+元數(shù)據(jù)+CQRS+DDD的應(yīng)用架構(gòu):擴(kuò)展性好儡遮,貫徹了OO思想乳蛾,有一套完整的規(guī)范標(biāo)準(zhǔn),并采用CQRS和領(lǐng)域建模技術(shù)鄙币,很大程度可降低應(yīng)用的復(fù)雜度肃叶。
層級(jí)職責(zé):
cola-adapter:負(fù)責(zé)對(duì)前端展示的路由和適配。
cola-app:負(fù)責(zé)獲取輸入十嘿,組裝context因惭,做輸入校驗(yàn),調(diào)用領(lǐng)域?qū)幼鰳I(yè)務(wù)處理绩衷。
cola-client:二方庫組件蹦魔,提供應(yīng)用對(duì)外的接口。
cola-domain:領(lǐng)域?qū)涌妊啵庋b核心業(yè)務(wù)邏輯勿决。
cola-infrastructure:基礎(chǔ)設(shè)施層,處理技術(shù)細(xì)節(jié)加領(lǐng)域防腐招盲。
start:spring boot啟動(dòng)層低缩。
圖來源:https://blog.csdn.net/significantfrank/article/details/110934799
CQRS架構(gòu)
命令查詢職責(zé)分離(CQRS)是指讀取和寫入分別擁有單獨(dú)的數(shù)據(jù)結(jié)構(gòu)。 使用CQRS理由是宪肖,在復(fù)雜領(lǐng)域中表制,使用單一模型處理讀取和寫入過于復(fù)雜健爬,我們可以通過分離模型來簡(jiǎn)化設(shè)計(jì)和實(shí)現(xiàn)。
其基本思想在于任何一個(gè)對(duì)象的方法可以分為以下兩類:
命令(Command):不返回任何結(jié)果(void)么介,但會(huì)改變對(duì)象的狀態(tài)娜遵。
查詢(Query):返回結(jié)果,但是不會(huì)改變對(duì)象的狀態(tài)壤短,對(duì)系統(tǒng)沒有副作用设拟。
CQRS查詢和更新數(shù)據(jù)(命令)模型不一樣,CQRS強(qiáng)調(diào)的是command和query訪問的數(shù)據(jù)模型不同久脯,分別根據(jù)command與query需求的不同特性設(shè)計(jì)數(shù)據(jù)模型纳胧。 命令模型數(shù)據(jù)變更后,需同步給查詢模型帘撰。
在很多系統(tǒng)業(yè)務(wù)設(shè)計(jì)上跑慕,數(shù)據(jù)庫里數(shù)據(jù)模型很難和業(yè)務(wù)領(lǐng)域模型一致,所以DDD中有領(lǐng)域模型(Domain model)的概念摧找,在領(lǐng)域?qū)愚D(zhuǎn)換成對(duì)應(yīng)的數(shù)據(jù)庫DO(Data Object)實(shí)體核行。
優(yōu)點(diǎn):
讀寫邏輯高度解耦,符合單一職責(zé)(模型分離蹬耘,可獨(dú)立設(shè)計(jì))
獨(dú)立部署有更好的伸縮性
缺點(diǎn):
帶來了復(fù)雜性芝雪,傳統(tǒng)過程式編程直接用一個(gè)模型搞定,現(xiàn)在變成兩個(gè)模型综苔,還要結(jié)合一些DDD的思想才能更好的實(shí)現(xiàn)惩系;
數(shù)據(jù)非強(qiáng)一致性,是最終一致性如筛。(個(gè)人理解:如果同步實(shí)現(xiàn)保證強(qiáng)一致性的話堡牡,命令端寫成功之后還需要往查詢端寫,會(huì)降低系統(tǒng)可用性)
幫助工程團(tuán)隊(duì)將函數(shù)編程原理應(yīng)用到高級(jí)設(shè)計(jì)和體系結(jié)構(gòu)與架構(gòu)的通俗易懂的思想和最佳實(shí)踐妙黍。
關(guān)于函數(shù)式編程或FP的許多文章都專注于低級(jí)編碼實(shí)踐(例如避免副作用)和FP特定模式(例如可怕的monad)悴侵。但是瞧剖,它們不涉及高級(jí)設(shè)計(jì)和體系結(jié)構(gòu)拭嫁。然而,F(xiàn)P原則可以大規(guī)模應(yīng)用抓于。實(shí)際上做粤,從后端的無服務(wù)器到前端的Redux / Elm風(fēng)格的框架,許多流行的框架和架構(gòu)樣式都源于函數(shù)式編程捉撮。
如果使用得當(dāng)怕品,F(xiàn)P原理可以降低復(fù)雜性,同時(shí)提高應(yīng)用程序的可測(cè)試性和可維護(hù)性巾遭。這是函數(shù)架構(gòu)肉康。
FP原理適用于軟件架構(gòu)
函數(shù)式編程的三個(gè)原理與軟件架構(gòu)特別相關(guān)闯估。首先是函數(shù)是獨(dú)立的值。也就是說吼和,可以像對(duì)待其他獨(dú)立值一樣對(duì)待它們涨薪,例如整數(shù)和字符串§排遥可以將它們分配給變量刚夺,存儲(chǔ)在列表中,作為參數(shù)傳遞末捣,作為結(jié)果返回等等侠姑。
在函數(shù)架構(gòu)中,基本單元也是函數(shù)箩做,但是我喜歡稱其為工作流workflow莽红,工作流是函數(shù)的基本單元。它可以稱為一個(gè)功能特性邦邦、用例船老、場(chǎng)景、故事或任何您想調(diào)用的圃酵。就像函數(shù)一樣在編碼級(jí)別是“某個(gè)事物”柳畔,這些工作流是架構(gòu)級(jí)別的“事物”,是架構(gòu)的基本構(gòu)建塊郭赐。
其次薪韩,組合是構(gòu)建系統(tǒng)的主要方式。只需將一個(gè)輸出連接到另一個(gè)輸入即可構(gòu)成兩個(gè)簡(jiǎn)單功能捌锭。結(jié)果是可以用作更多合成的起點(diǎn)的另一個(gè)功能俘陷。
組合是一個(gè)非常重要的概念,函數(shù)式程序員擁有一套標(biāo)準(zhǔn)工具观谦,例如monads拉盾,即使輸入和輸出不完全匹配也可以進(jìn)行組合。
從體系結(jié)構(gòu)和架構(gòu)的角度來看豁状,由較小的函數(shù)組成較大的函數(shù)捉偏,其結(jié)果最明顯是,函數(shù)系統(tǒng)看起來像帶有輸入和輸出的管道泻红,而不是面向消息的請(qǐng)求/響應(yīng)模型夭禽。
[圖片上傳失敗...(image-f41b4d-1644728141682)]
每個(gè)工作流程函數(shù)通常具有相同的結(jié)構(gòu):讀取數(shù)據(jù)、制定業(yè)務(wù)決策并根據(jù)需要轉(zhuǎn)換數(shù)據(jù)谊路,最后讹躯,在另一端輸出任何新數(shù)據(jù)或事件。這些步驟中的每一個(gè)都可以依次視為較小的函數(shù)。分支和其他類型的復(fù)雜性可能會(huì)發(fā)揮作用潮梯,但是即使工作流程變得越來越大和越來越復(fù)雜骗灶,數(shù)據(jù)也始終會(huì)朝一個(gè)方向流動(dòng)。
這種組合方法意味著我們僅結(jié)合了特定業(yè)務(wù)工作流所需的特定組件秉馏。不需要傳統(tǒng)的分層體系結(jié)構(gòu)矿卑。當(dāng)我們向系統(tǒng)中添加新功能時(shí),每個(gè)新工作流程所需的函數(shù)都是獨(dú)立定義的沃饶,而不是分組為數(shù)據(jù)庫或服務(wù)層母廷。
如果我們確實(shí)需要在不同的工作流中使用完全相同的函數(shù),則可以將該函數(shù)一次定義為子函數(shù)糊肤,然后在需要它的工作流中將其重新用作共享步驟琴昆。這就是組合方法如此吸引人的原因:工作流作為獨(dú)立的單元進(jìn)行設(shè)計(jì)和構(gòu)建,僅包含其所需的功能馆揉,但是當(dāng)需要時(shí)业舍,我們?nèi)匀豢梢岳弥赜煤徒M件化的所有好處。
最后升酣,函數(shù)式程序員嘗試盡可能多地使用純函數(shù)舷暮。純函數(shù)是確定性的(給定的輸入始終會(huì)導(dǎo)致相同的輸出),并且沒有副作用(例如突變或I / O)噩茄。它們非常易于測(cè)試(確定性O旅妗),并且易于理解而無需深入研究其實(shí)現(xiàn)(無副作用<ㄆ浮)沥割。
與外界互動(dòng)
您不需要鼓勵(lì)開發(fā)人員使用洋蔥架構(gòu),作為FP 方法的副作用它會(huì)自動(dòng)發(fā)生凿菩。
當(dāng)然机杜,在某些時(shí)候,我們將需要進(jìn)行I / O操作-讀寫文件衅谷,訪問數(shù)據(jù)庫等等椒拗。函數(shù)式程序員試圖將這種不確定性盡可能地保持在管道的邊緣。某些語言(例如Elm和Haskell)對(duì)此非常嚴(yán)格获黔,不允許有任何偏差蚀苛,而其他語言則將其更多地視為準(zhǔn)則而不是規(guī)則。
該函數(shù)模型與眾所周知的方法非常相似肢执,例如洋蔥架構(gòu)枉阵,六邊形架構(gòu)(也稱為端口和適配器架構(gòu))以及函數(shù)核心译红,命令式外殼预茄。在所有情況下,核心域(純業(yè)務(wù)邏輯)都與基礎(chǔ)架構(gòu)隔離〕苌拢基礎(chǔ)結(jié)構(gòu)代碼了解核心域拙徽,但并非相反。依賴關(guān)系是單向的诗宣,I / O保持在邊緣膘怕。
[圖片上傳失敗...(image-78e868-1644728141682)]
僅將純代碼用于業(yè)務(wù)邏輯意味著單元測(cè)試和集成測(cè)試之間存在明顯的區(qū)別。單元測(cè)試是針對(duì)核心領(lǐng)域的召庞,它是確定性和快速的岛心,而集成測(cè)試則是從頭到尾對(duì)工作流進(jìn)行的。
函數(shù)式編程的優(yōu)點(diǎn)之一是篮灼,業(yè)務(wù)域與基礎(chǔ)結(jié)構(gòu)的這種隔離是自然發(fā)生的忘古。您不需要鼓勵(lì)開發(fā)人員使用洋蔥體系結(jié)構(gòu)。作為FP 方法的副作用它會(huì)自動(dòng)發(fā)生诅诱。
邊界和背景
作為軟件設(shè)計(jì)師和架構(gòu)師髓堪,我們的下一個(gè)挑戰(zhàn)是決定如何將這些工作流或管道分組為邏輯單元。與往常一樣娘荡,這更多的是藝術(shù)而不是科學(xué)干旁。
有許多準(zhǔn)則可以提供幫助。低耦合和高內(nèi)聚的經(jīng)典原理不僅適用于函數(shù)代碼炮沐,而且適用于面向?qū)ο蟮拇a争群。或者大年,重述通用封閉原則:一起變化的代碼應(yīng)該一起生活祭阀。最近,領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)(DDD)社區(qū)非常重視組件邊界以及在何處繪制邊界鲜戒。
[圖片上傳失敗...(image-2be33b-1644728141682)]
在DDD術(shù)語中专控,相關(guān)函數(shù)的分組稱為有界上下文,每個(gè)有界上下文在其自身權(quán)限中都被視為一個(gè)微型域遏餐。它通常對(duì)應(yīng)于特定業(yè)務(wù)功能的邏輯封裝伦腐。我們用“有界上下文”而不是像之前稱為“子系統(tǒng)”,是因?yàn)樗刮覀兡軌驅(qū)W⒂谠O(shè)計(jì)解決方案時(shí)最重要的事情:了解上下文和邊界失都。(如何定義這些有界上下文正是另一篇文章的主題柏蘑。)
為什么要上下文?因?yàn)槊總€(gè)上下文代表一些專業(yè)知識(shí)或能力粹庞。在上下文中咳焚,我們共享一種通用語言,并且設(shè)計(jì)是連貫一致的庞溜。但是革半,就像在現(xiàn)實(shí)世界中一樣碑定,從上下文中提取信息可能會(huì)造成混亂或無法使用。
太寬或太模糊的邊界根本就沒有邊界又官。
為什么要有邊界延刘?在現(xiàn)實(shí)世界中,領(lǐng)域可能具有模糊邊界六敬。但是在軟件領(lǐng)域碘赖,我們希望減少子系統(tǒng)之間的耦合,以便它們可以獨(dú)立發(fā)展外构。邊界是確保子系統(tǒng)保持獨(dú)立的關(guān)鍵普泡,可以使用標(biāo)準(zhǔn)軟件實(shí)踐(例如使用顯式API)和避免依賴項(xiàng)(例如共享代碼)來維護(hù)子系統(tǒng)。在需求不斷變化的復(fù)雜項(xiàng)目中审编,我們必須毫不留情地維護(hù)有界上下文的“有界”部分劫哼。太寬或太模糊的邊界根本就沒有邊界。
自治是有界上下文的關(guān)鍵方面割笙。具有自主權(quán)意味著有界上下文可以做出決策权烧,而不必等待來自其他有界上下文的決策或信息。也就是說伤溉,如果一個(gè)有界上下文不可用般码,則其他有界上下文可以繼續(xù)獨(dú)立運(yùn)行,這是重要的分離乱顾。
自治也可以應(yīng)用于開發(fā)過程板祝。通常,一個(gè)有界上下文最好由一個(gè)團(tuán)隊(duì)擁有走净。想想三腿比賽:綁在腿上的兩個(gè)跑步者比自由地獨(dú)立跑步的兩個(gè)跑步者慢得多券时。軟件組件也是如此。如果兩個(gè)團(tuán)隊(duì)在相同的受限環(huán)境中做出貢獻(xiàn)伏伯,那么他們可能最終會(huì)隨著設(shè)計(jì)的發(fā)展而朝著不同的方向拉動(dòng)設(shè)計(jì)橘洞。
如果將有界上下文的概念應(yīng)用于函數(shù)體系結(jié)構(gòu),我們最終會(huì)遇到許多小型的说搅、集中的域炸枣,每個(gè)域都支持許多業(yè)務(wù)工作流。這些邊界的定義方式應(yīng)使其內(nèi)部的工作流具有自主性弄唧,并且能夠在不依賴其他系統(tǒng)的情況下完成其工作适肠。
在某些情況下,長(zhǎng)期運(yùn)行的用例或場(chǎng)景需要多個(gè)工作流候引。在這種情況下侯养,處于不同上下文中的工作流將需要使用事件和其他方法相互通信。但是澄干,重要的是將單個(gè)工作流保持在一個(gè)有界上下文中逛揩,并且不要嘗試實(shí)現(xiàn)方案在多種情況下“端到端”柠傍。允許工作流到達(dá)多個(gè)服務(wù)內(nèi)部最終將導(dǎo)致我們很好解耦的體系結(jié)構(gòu)演變成無法維護(hù)的依賴關(guān)系的糾結(jié)- “ 一個(gè)大麻煩” ∠⒊撸”
實(shí)體服務(wù)反模式
定義邊界可能沒有正確的方法携兵,但是肯定有許多錯(cuò)誤的方法疾掰。
定義邊界可能沒有一種正確的方法搂誉,但是肯定有許多錯(cuò)誤的方法。分組功能的常見反模式是“ 實(shí)體服務(wù)”方法静檬,其中圍繞實(shí)體而不是工作流構(gòu)建服務(wù)炭懊。也就是說,有一個(gè)“訂單”服務(wù)拂檩,一個(gè)“產(chǎn)品”服務(wù)侮腹,等等。這通常是由于天真地將面向?qū)ο蟮脑O(shè)計(jì)直接轉(zhuǎn)移到面向工作流程的體系結(jié)構(gòu)而導(dǎo)致的稻励。此設(shè)計(jì)的主要問題是父阻,單個(gè)業(yè)務(wù)工作流通常將需要所有這些服務(wù)進(jìn)行協(xié)作。如果其中任何一個(gè)不可用望抽,則整個(gè)工作流程將失敗加矛。而且,如果工作流程需要發(fā)展煤篙,我們可能需要同時(shí)觸摸和更新許多服務(wù)中的代碼斟览,從而破壞了“一起改變的代碼應(yīng)該一起生活”的規(guī)則。
此外辑奈,僅因?yàn)闃I(yè)務(wù)工作流程涉及實(shí)體苛茂,例如“訂單”并不意味著它與使用該實(shí)體的其他工作流程有任何共同點(diǎn)。例如鸠窗,“支付訂單”工作流和“刪除訂單”工作流都涉及訂單妓羊,但是具有完全不同的業(yè)務(wù)邏輯。不需要他們都依賴于將不同函數(shù)集合在一起的“訂購”服務(wù)稍计。當(dāng)其他要求(例如安全性侍瑟,可伸縮性等)開始發(fā)揮作用時(shí),我們可能會(huì)發(fā)現(xiàn)必須以非常不同的方式來管理不同的工作流程丙猬。耦合它們只會(huì)引起痛苦涨颜!
事件
現(xiàn)在,我們的工作流函數(shù)已分組到有界上下文中茧球,可以使用了庭瑰。但是什么觸發(fā)了這些業(yè)務(wù)工作流?是什么導(dǎo)致員工抢埋,用戶或自動(dòng)化流程啟動(dòng)工作流弹灭?
是一個(gè)事件督暂。也就是說,在外面的世界有新的變化-客戶在點(diǎn)擊一個(gè)按鈕穷吮,郵件到達(dá)時(shí)逻翁,警報(bào)彈出。這是在一個(gè)商業(yè)活動(dòng)的形式捕獲-例如捡鱼,“下訂單”或“已收到電子郵件八回。” 在FP架構(gòu)中驾诈,像這樣的業(yè)務(wù)事件會(huì)觸發(fā)工作流缠诅。
此外,使用這種方法乍迄,工作流的輸出也是一個(gè)事件:一個(gè)通知管引,通知所有下游工作流世界上發(fā)生了重要變化。特定于特定工作流程且未共享的更改(例如數(shù)據(jù)庫更新)不會(huì)作為事件從工作流程中發(fā)出闯两。
這就是我們可以從這些較小的工作流程中組裝較大的流程的方式褥伴。每個(gè)工作流程都由一個(gè)事件觸發(fā),并且該工作流程又會(huì)生成更多事件供下游流程使用漾狼。但是事件如何在工作流之間傳遞重慢?這取決于項(xiàng)目的特定要求。如果所有工作流程都可以生活在同一個(gè)流程中邦投,那么它可以是一個(gè)簡(jiǎn)單的內(nèi)存隊(duì)列伤锚。但是,如果需要分別和獨(dú)立地部署工作流志衣,則首選外部隊(duì)列屯援,服務(wù)總線或Kafka風(fēng)格的事件日志。
請(qǐng)注意念脯,在所有情況下狞洋,工作流都是異步交互的。這使他們?cè)跁r(shí)間和空間上保持獨(dú)立和分離绿店。很少使用在工作流中直接調(diào)用另一個(gè)工作流的命令和控制方法吉懊。
如果您熟悉事件驅(qū)動(dòng)的體系結(jié)構(gòu),則將很熟悉這種基于事件的方法假勿。而且借嗽,確實(shí),具有單獨(dú)管道的FP方法非常適合于這種體系結(jié)構(gòu)樣式转培。
邏輯架構(gòu)與物理架構(gòu)
對(duì)事件觸發(fā)的單獨(dú)工作流的描述是邏輯視圖恶导,而不是物理視圖。
到目前為止浸须,設(shè)計(jì)目標(biāo)已經(jīng)圍繞業(yè)務(wù)需求進(jìn)行了調(diào)整惨寿,但是我們還需要考慮技術(shù)需求邦泄。贊成使用工作流作為設(shè)計(jì)的單元的一個(gè)觀點(diǎn)是:它們可以在物理上部署在多種方式-例如作為微服務(wù),獨(dú)立無服務(wù)器函數(shù)裂垦,或單體架構(gòu)的組件模塊化顺囊,或甚至作為在基于代理的系統(tǒng)Erlang或Akka的風(fēng)格。技術(shù)實(shí)現(xiàn)的選擇取決于開發(fā)團(tuán)隊(duì)的規(guī)模和數(shù)量蕉拢,安全性特碳,可伸縮性需求等。
在某些情況下企量,邏輯工作流也可能在物理上分為單獨(dú)的部分测萎。例如亡电,工作流可以通過從前端開始同步API 調(diào)用届巩,然后繼續(xù)在后端執(zhí)行。
那么份乒,應(yīng)該清楚的是恕汇,系統(tǒng)的邏輯和物理組成是不相同的,不應(yīng)混為一談或辖。但這并不意味著我們可以避免就架構(gòu)的物理和實(shí)現(xiàn)方面做出決策瘾英。單體或無服務(wù)器是在項(xiàng)目早期應(yīng)考慮的重要決定。同樣颂暇,編程語言的選擇和數(shù)據(jù)庫的選擇也沒有反映在像這樣的邏輯模型中缺谴,但它們是至關(guān)重要的體系結(jié)構(gòu)決策。
前端函數(shù)架構(gòu)
隨著SPA的興起以及完全寫在前端的嚴(yán)肅應(yīng)用程序耳鸯,前端軟件體系結(jié)構(gòu)變得越來越重要湿蛔。隨著FP強(qiáng)調(diào)不變性、單向數(shù)據(jù)流和邊緣處的I / O已被證明對(duì)于降低復(fù)雜性很有價(jià)值县爬,用于前端架構(gòu)的函數(shù)性方法也變得越來越流行阳啥。
最常見的功能前端體系結(jié)構(gòu)是Model-View-Update體系結(jié)構(gòu),也稱為Elm體系結(jié)構(gòu)财喳。在此設(shè)計(jì)中察迟,應(yīng)用程序包含一個(gè)不變的模型(代表應(yīng)用程序狀態(tài))和兩個(gè)關(guān)鍵功能:一個(gè)update功能是在發(fā)生來自瀏覽器的消息或事件(例如單擊按鈕)時(shí)更新模型的view功能,以及一個(gè)呈現(xiàn)查看該模型(通常只是HTML)耳高。呈現(xiàn)的視圖可以將瀏覽器事件與域模型中定義的消息相關(guān)聯(lián)扎瓶,以便隨后在瀏覽器中單擊按鈕(例如,在瀏覽器中)將觸發(fā)域消息泌枪,從而觸發(fā)更新功能概荷,該函數(shù)最終將呈現(xiàn)新視圖,即再次傳遞給瀏覽器工闺。以此類推乍赫。
函數(shù)式編程原理的力量
從后端的微服務(wù)和無服務(wù)器到前端的MVU瓣蛀,函數(shù)式編程的知識(shí)對(duì)于理解許多現(xiàn)代架構(gòu)樣式至關(guān)重要。
軟件體系結(jié)構(gòu)的許多良好做法:凝聚力雷厂,去耦惋增,I / O的隔離等等,從應(yīng)用編程函數(shù)的原則自然會(huì)出現(xiàn)改鲫。例如诈皿,我們已經(jīng)看到,在典型的函數(shù)設(shè)計(jì)中像棘,每個(gè)工作流都是獨(dú)立構(gòu)建的稽亏,僅包含其所需的功能(最大限度地提高了凝聚力),并且在各個(gè)層次上都強(qiáng)調(diào)了自治性缕题,從單個(gè)函數(shù)到有界上下文(去耦) 截歉。此外,確毖塘悖可測(cè)試性和可維護(hù)性的一種確保方法是將業(yè)務(wù)邏輯保持在純確定性的函數(shù)中瘪松,并使用不可變的數(shù)據(jù)模型來迫使數(shù)據(jù)更改變得明確。
有許多方法可以進(jìn)行軟件體系結(jié)構(gòu)锨阿,并且沒有一種千篇一律的方法宵睦。但是,使用函數(shù)式編程原理的潛在好處是多方面的墅诡,我鼓勵(lì)您進(jìn)一步研究這些原理-甚至可以將其應(yīng)用于下一個(gè)項(xiàng)目壳嚎。祝好運(yùn)!