如何基于DDD構(gòu)建微服務(wù)架構(gòu)

微服務(wù)構(gòu)建本質(zhì)上是軟件構(gòu)建過程中長期演進積累的一系列理念、架構(gòu)原則、工具和最佳實踐。
領(lǐng)域驅(qū)動設(shè)計的軟件思想體系和方法論可以用于指導(dǎo)微服務(wù)建模、微服務(wù)劃分、微服務(wù)架構(gòu)設(shè)計等相關(guān)工作,它可以促使技術(shù)人員與領(lǐng)域?qū)<疫_成共識,構(gòu)建領(lǐng)域邊界合理、具備明確界限上下文、關(guān)注點分離、獨立自治的微服務(wù)。
01 領(lǐng)域驅(qū)動設(shè)計概述
領(lǐng)域驅(qū)動設(shè)計(Domain Driven Design)概念的興起可以追溯到1986年,《人月神話》的作者Brooks提出軟件的本質(zhì)復(fù)雜性(Essential Complexity)存在于復(fù)雜的業(yè)務(wù)領(lǐng)域中,技術(shù)僅僅是輔助工具,它解決的問題是幫助業(yè)務(wù)領(lǐng)域從現(xiàn)實問題映射轉(zhuǎn)換成軟件實現(xiàn)。
領(lǐng)域驅(qū)動設(shè)計在戰(zhàn)略設(shè)計層面,從業(yè)務(wù)視角出發(fā)使技術(shù)人員專注于問題域,從領(lǐng)域?qū)<夷抢铽@得領(lǐng)域見解,通過模塊劃分建立領(lǐng)域服務(wù)邊界,通過界限上下文明確服務(wù)的職責(zé)。
領(lǐng)域驅(qū)動設(shè)計在戰(zhàn)術(shù)設(shè)計層面,從技術(shù)的視角出發(fā),提煉有效的業(yè)務(wù)模型,實施領(lǐng)域建模、架構(gòu)設(shè)計完成軟件的落地。
領(lǐng)域驅(qū)動設(shè)計通過隔離業(yè)務(wù)與技術(shù)的復(fù)雜性,成為程式化、標(biāo)準化的軟件架構(gòu)設(shè)計范式。
軟件復(fù)雜度的來源
業(yè)務(wù)的復(fù)雜性:業(yè)務(wù)的復(fù)雜性體現(xiàn)在業(yè)務(wù)流程不清晰、業(yè)務(wù)參與人員多、業(yè)務(wù)與技術(shù)耦合等方面。在業(yè)務(wù)的早期階段,為了快速滿足功能需求容易形成面條式的代碼風(fēng)格,這樣的代碼風(fēng)格會導(dǎo)致軟件模塊膨脹、開發(fā)效率降低、功能擴展步伐放緩、業(yè)務(wù)模型與代碼脫節(jié)等。 技術(shù)的復(fù)雜性:技術(shù)的復(fù)雜性來源于對項目的質(zhì)量屬性需求,諸如系統(tǒng)的性能、客戶體驗、服務(wù)高可用性等。為解決服務(wù)的響應(yīng)延遲、吞吐、安全等問題,我們會引入緩存、消息隊列、第三方模塊組件,而這些技術(shù)的整合給系統(tǒng)引入了額外的復(fù)雜性和技術(shù)挑戰(zhàn)。
質(zhì)量屬性需求:系統(tǒng)非功能(也叫非行為)部分的需求。
領(lǐng)域驅(qū)動解決之道
解決這種軟件構(gòu)建中面臨的復(fù)雜性問題,我們需要從領(lǐng)域開始著手,與業(yè)務(wù)專家一起獲得領(lǐng)域見解,促使軟件利益干系方在領(lǐng)域內(nèi)建立通用語言。技術(shù)人員通過建模的手段提煉出事物的本質(zhì),以便更好地指導(dǎo)應(yīng)用系統(tǒng)的構(gòu)建和規(guī)劃。
領(lǐng)域驅(qū)動設(shè)計中包含了大量成熟的理論、概念、模式和架構(gòu),它包含一套解決復(fù)雜領(lǐng)域模型的軟件架構(gòu)方法,思想是圍繞業(yè)務(wù)模型來連接和實現(xiàn)核心業(yè)務(wù)概念。
領(lǐng)域驅(qū)動設(shè)計可以讓業(yè)務(wù)和技術(shù)的變化產(chǎn)生的不可預(yù)知因素互相分離,將人員變動、團隊規(guī)模、協(xié)作溝通等外界因素變化對產(chǎn)品和項目的影響封裝在一個可控的容器和框架下,從而解決軟件面臨的復(fù)雜性問題,如下圖所示。
事務(wù)腳本模式與領(lǐng)域建模模式
事務(wù)腳本模式:事物腳本模式常見于單體應(yīng)用中,它將所有邏輯全部組織在一個單一過程方法中,從數(shù)據(jù)庫的調(diào)用到不同業(yè)務(wù)邏輯、策略的執(zhí)行全部集成在一個大的方法塊中。它的好處是簡單、容易實現(xiàn),它的缺點是沒有自己的狀態(tài),也無法擴展,容易將服務(wù)組件與數(shù)據(jù)存儲模型之間的剛性依賴引入業(yè)務(wù)邏輯中。 領(lǐng)域建模模式:領(lǐng)域建模模式將業(yè)務(wù)邏輯轉(zhuǎn)移到了領(lǐng)域?qū)ο螅―omain Object)中,每個領(lǐng)域?qū)ο笸瓿蓪儆谧约旱臉I(yè)務(wù)行為。同時數(shù)據(jù)存儲層的邏輯也變得相對簡單,數(shù)據(jù)庫不再參與領(lǐng)域模型的業(yè)務(wù)邏輯,而是回歸數(shù)據(jù)“持久化”的本質(zhì)。
使用領(lǐng)域模式可以提升系統(tǒng)的內(nèi)聚性和可重用性,通過不同類之間的協(xié)同完成所有功能。另外,多態(tài)的模式也讓擴展新的策略更加方便,業(yè)務(wù)語義更加通用、顯性化。領(lǐng)域建模過程遵循“SOLID”原則并實現(xiàn)業(yè)務(wù)域的邏輯解決方案。
說明:SOLID原則
\1. Single Responsibility Principle:單一職責(zé)原則
\2. Open Closed Principle:開閉原則
\3. Liskov Substitution Principle:里氏替換原則
\4. Interface Segregation Principle:接口隔離原則
\5. Dependence Inversion Principle:依賴倒置原則
領(lǐng)域驅(qū)動設(shè)計核心要素
如下圖所示是領(lǐng)域驅(qū)動設(shè)計的核心要素,包含領(lǐng)域驅(qū)動設(shè)計中的通用模型術(shù)語和重要的戰(zhàn)術(shù)模式。這些模式不僅可以捕獲和傳遞領(lǐng)域中的概念、關(guān)系及邏輯,也能幫助我們管理業(yè)務(wù)的復(fù)雜性并確保領(lǐng)域模型的行為清晰明確。
領(lǐng)域:相對于軟件系統(tǒng)來說就是系統(tǒng)要解決的現(xiàn)實問題。 子域:對于領(lǐng)域進行不同維度切分的相對內(nèi)聚的子系統(tǒng)單元。 分層架構(gòu):通過分層架構(gòu)將業(yè)務(wù)域和技術(shù)邏輯域隔離。 服務(wù):服務(wù)通常是領(lǐng)域?qū)ο蟮恼{(diào)用方,用來協(xié)調(diào)領(lǐng)域?qū)ο笸瓿芍付I(yè)務(wù)邏輯職責(zé)。 實體:實體與面向?qū)ο笾械母拍铑愃疲穷I(lǐng)域模型的基本元素,在領(lǐng)域模型中,實體應(yīng)該具有唯一的標(biāo)識符。 值對象:值對象是沒有唯一標(biāo)識符的實體。值對象在領(lǐng)域模型中是可以被共享的,它們應(yīng)該是不可變的,當(dāng)有其他地方需要用到值對象時,可以將它的副本作為參數(shù)傳遞。 聚合:聚合使用邊界將內(nèi)部和外部的對象劃分開來。每個聚合有一個根,這個根是一個實體作為外部可以訪問的唯一對象。 資源庫:是封裝的所有獲取對象引用所需的邏輯單元。 工廠:工廠用來封裝對象創(chuàng)建所必需的信息,當(dāng)聚合根建立時,所有聚合包含的對象將隨之建立。
02 專注問題域
解決一個業(yè)務(wù)場景中的復(fù)雜問題從理解問題域開始,通過專注于問題域并理解復(fù)雜問題背后的實質(zhì),你才能設(shè)計有效的模型來應(yīng)對業(yè)務(wù)的挑戰(zhàn)。
在項目初期,盡量避免沉溺于技術(shù)實現(xiàn),而要把焦點集中在問題領(lǐng)域,不要忘記技術(shù)服務(wù)業(yè)務(wù)的原則。
理解問題域
我們以一個金融場景下的“業(yè)務(wù)運營監(jiān)控系統(tǒng)”為例進行分析。經(jīng)過與運營管理專家和相關(guān)業(yè)務(wù)方的多輪需求探論,我們初步了解了用戶的業(yè)務(wù)訴求和痛點。需要強調(diào)的是對于問題域的充分理解是我們的首要任務(wù)。
這里整理了一份需求文檔,它詳細地記錄了問題域的具體范圍和詳細需求。這份文檔不僅是業(yè)務(wù)與技術(shù)團隊之間的一份溝通文檔,也可以作為軟件生命周期在需求分析階段的一個清晰的、規(guī)范化的知識協(xié)作產(chǎn)物。
提煉問題域
理解復(fù)雜問題并從中識別、提煉出關(guān)鍵的業(yè)務(wù)模型,即提煉問題域是領(lǐng)域驅(qū)動設(shè)計的關(guān)鍵環(huán)節(jié)。團隊可以通過頭腦風(fēng)暴的形式羅列出領(lǐng)域中的所有事件,整合之后形成最終的領(lǐng)域事件集合。
你需要在關(guān)鍵事件標(biāo)記的范圍里,參照不同利益干系方的業(yè)務(wù)訴求,組織領(lǐng)域事件和模型,同時,你需要整理出與項目關(guān)聯(lián)的上下游系統(tǒng),如下圖所示。
通過挖掘隱藏在領(lǐng)域事件中的核心領(lǐng)域模型,我們可以找到從問題空間到方案空間的對應(yīng)映射關(guān)系。針對上述業(yè)務(wù)監(jiān)控系統(tǒng)案例,“進件存量”和“進件流量”的概念成為我們發(fā)現(xiàn)的重要領(lǐng)域模型。
進件存量:是指在某一指定的時間點,過去生產(chǎn)與積累起來的進件的結(jié)存數(shù)量。
進件流量:單位時間內(nèi)流過某一段管道的進件體積流量。
作為衡量業(yè)務(wù)系統(tǒng)運轉(zhuǎn)狀態(tài)的重要指標(biāo),業(yè)務(wù)的“存量”狀態(tài)可以表示業(yè)務(wù)的積壓情況,而業(yè)務(wù)的“流量”狀態(tài)可以表示業(yè)務(wù)流轉(zhuǎn)的變化情況。
如下圖所示是我們總結(jié)的監(jiān)控系統(tǒng)概要視圖,其中實線表示的是城市信貸業(yè)務(wù)工作流中進件在不同系統(tǒng)的流向,而虛線表示的則是業(yè)務(wù)的存量、流量在業(yè)務(wù)監(jiān)控系統(tǒng)的事件記錄。
03 服務(wù)的拆分
完成問題域的理解和提煉后,我們需要對整體系統(tǒng)做進一步的服務(wù)拆分。下圖是我們根據(jù)業(yè)務(wù)領(lǐng)域能力對“業(yè)務(wù)運營監(jiān)控系統(tǒng)”進行拆分后的子領(lǐng)域服務(wù)及模塊劃分說明。
業(yè)務(wù)事件收集(如下圖和表所示)
事件過濾聚合(如下圖和表所示)
規(guī)則配置(如下圖和表所示)
監(jiān)控查詢展示(如下圖和表所示)
為什么要做服務(wù)拆分
降低系統(tǒng)的整體復(fù)雜性:根據(jù)業(yè)務(wù)領(lǐng)域進行合理的服務(wù)拆分是一個有效控制系統(tǒng)復(fù)雜性的方法。 提高效率:服務(wù)拆分后,代碼模塊相互隔離,并發(fā)的開發(fā)模式可以提升開發(fā)人員的效率。 團隊人員各司其職:拆分的項目可分派給擅長相關(guān)方面技術(shù)的人員,讓團隊成員各司其職,降低工作的耦合度。 共享和自治:可以通過定義好的服務(wù)接口進行服務(wù)共享,同時拆分后的服務(wù)也更加自治。 解決依賴問題:通過服務(wù)拆分,可以清晰地了解哪些服務(wù)依賴會對業(yè)務(wù)造成影響,從而準備預(yù)案。
服務(wù)拆分的依據(jù)
高內(nèi)聚、低耦合是服務(wù)拆分的主要依據(jù),下面我們列舉一些常用的服務(wù)拆分策略,了解如何對單體架構(gòu)進行拆分。
區(qū)分服務(wù)類型:工具服務(wù)區(qū)別于業(yè)務(wù)服務(wù),它的特點是與業(yè)務(wù)領(lǐng)域無關(guān),根據(jù)其用途可以進一步細分,一般包括的形式有公共工具服務(wù)、資源工具服務(wù)、包裝器服務(wù)等。 根據(jù)功能定義劃分服務(wù):領(lǐng)域驅(qū)動設(shè)計通過分析問題空間和業(yè)務(wù)邏輯,將應(yīng)用程序定義為域,域由多個子域組成,每個子域?qū)?yīng)于業(yè)務(wù)的不同功能部分。 根據(jù)技術(shù)邊界劃分服務(wù):對于產(chǎn)品類型的服務(wù)使用技術(shù)能力劃分服務(wù)邊界,前后端分離架構(gòu)就是通過技術(shù)棧劃分服務(wù)邊界的典型架構(gòu)模式。
服務(wù)拆分范式
通過增加服務(wù)實例或者機器來解決服務(wù)的容量和可用性問題是常用的可擴展架構(gòu)解決方案。在《可擴展藝術(shù)》一書中提出了系統(tǒng)的可擴展性模型:AKF可擴展立方,可以作為服務(wù)拆分的范式。
AKF可擴展立方:描述從單體應(yīng)用到分布式可擴展應(yīng)用的可擴展模型。
如下圖所示是使用Scale Cube的3D模型實現(xiàn)的一個微服務(wù)架構(gòu)模型,在X軸上通過API網(wǎng)關(guān)進行水平擴展,在Y軸上進行單體拆分后的微服務(wù)構(gòu)建,服務(wù)之間可以通過REST API進行簡單交互,Z軸是數(shù)據(jù)維度的拆分。
X軸:服務(wù)擴展,通過克隆的方式水平擴展。一般是負載均衡后運行多個應(yīng)用副本,達到某個服務(wù)的高吞吐量和高可用性。 Y軸:功能拆分,通過拆分不同的事務(wù)進行擴展。微服務(wù)對應(yīng)著Y軸,即將單體應(yīng)用拆分為微服務(wù)應(yīng)用。 Z軸:數(shù)據(jù)分區(qū),通過分隔相同的事務(wù)進行擴展,例如數(shù)據(jù)庫分庫分表。
總之,服務(wù)支持水平擴展以提升容量;對功能的拆分體現(xiàn)在對業(yè)務(wù)模型的切入和深入理解上;應(yīng)用數(shù)據(jù)的劃分是微服務(wù)的重要原則,如果數(shù)據(jù)的耦合問題無法解決,那么應(yīng)用服務(wù)的劃分還會有代碼耦合和級聯(lián)影響。
04 界限上下文
在找到服務(wù)邊界并把系統(tǒng)拆分后,我們需要使用“界限上下文”的概念明確服務(wù)之間的交互共享模型和行為接口,它不僅可以有效地限定領(lǐng)域的職責(zé)邊界和特性范圍,也可以控制問題域的規(guī)模,進而以化整為零的方式控制整個系統(tǒng)的復(fù)雜性。
在業(yè)務(wù)運營監(jiān)控項目中,存量項模型作為業(yè)務(wù)過濾聚合服務(wù)和存量查詢統(tǒng)計服務(wù)的共享模型,關(guān)系如下圖所示。
為了實現(xiàn)捕獲和統(tǒng)計監(jiān)控業(yè)務(wù)運營過程中的不同階段存量的業(yè)務(wù)狀態(tài),我們將存量項作為上述兩個服務(wù)上下文的共享模型,但我們不會暴露“過濾聚合服務(wù)”中的存量明細、Flow、Stream等模塊的實現(xiàn)細節(jié)。
作為兩個獨立的服務(wù)主體,它們應(yīng)該在邊界上有明確的界線劃分和通信機制。如果服務(wù)邊界與領(lǐng)域的界限上下文能夠保持一致,那么我們已經(jīng)為高內(nèi)聚、低耦合的微服務(wù)架構(gòu)實現(xiàn)了關(guān)鍵的一步。
05 領(lǐng)域建模
領(lǐng)域建模是領(lǐng)域驅(qū)動設(shè)計的核心,通過領(lǐng)域模型可以封裝對業(yè)務(wù)的抽象,建立業(yè)務(wù)概念與領(lǐng)域規(guī)則的關(guān)系。領(lǐng)域模型更關(guān)注的是業(yè)務(wù)語義的顯性表達,而不是具體的數(shù)據(jù)存儲及代碼邏輯實現(xiàn)細節(jié),它可以有效地降低業(yè)務(wù)人員和技術(shù)人員之間的溝通成本。
案例分析
回到“業(yè)務(wù)運營監(jiān)控系統(tǒng)”中,我們把業(yè)務(wù)監(jiān)控的核心訴求聚焦在“業(yè)務(wù)事件”,以及業(yè)務(wù)的存量和流量領(lǐng)域模型。
在整理了領(lǐng)域服務(wù)的核心模塊后,我們可以把業(yè)務(wù)方關(guān)注的組織信息、業(yè)務(wù)類型信息、業(yè)務(wù)階段信息進行進一步領(lǐng)域模型細化,如下圖所示。
BizEvent:業(yè)務(wù)事件是業(yè)務(wù)監(jiān)控的數(shù)據(jù)源,使用統(tǒng)一的JSON格式記錄消息事件,以日志方式封裝當(dāng)前業(yè)務(wù)系統(tǒng)發(fā)生的事件詳情。 Stream:對應(yīng)一個端到端的數(shù)據(jù)流轉(zhuǎn)概念,通常我們會將BizEvent事件發(fā)送到Kafka的一個Topic上,通過建立Stream可以在消費端處理指定Topic上的數(shù)據(jù)流。 Flow:Flow對應(yīng)一個監(jiān)控業(yè)務(wù)計算邏輯,存量Flow可以統(tǒng)計對應(yīng)的存量狀態(tài),流量Flow統(tǒng)計當(dāng)前業(yè)務(wù)的流量狀態(tài)。 Service:它并非領(lǐng)域?qū)ο螅硎疽粋€通用的服務(wù)層,Service提供業(yè)務(wù)存量和流量的查詢、備份、預(yù)警等業(yè)務(wù)方法。 Provision:用戶配置前置通用服務(wù),不對應(yīng)領(lǐng)域?qū)ο螅饕邮沼脩舻呐渲谜埱螅⒈4鏋闃I(yè)務(wù)規(guī)則。 Rule:即規(guī)則模型,屬于核心領(lǐng)域模型,業(yè)務(wù)方可以通過它靈活地定制關(guān)心的業(yè)務(wù)狀態(tài)并進行預(yù)警、過濾等。 Detail:屬于業(yè)務(wù)的中間監(jiān)控過程詳情,屬于領(lǐng)域?qū)ο螅瑫r包含組織、階段、業(yè)務(wù)類型等明細對象屬性(Org、Phase、BizType)。
使用領(lǐng)域建模的設(shè)計方法可以進一步將“業(yè)務(wù)監(jiān)控系統(tǒng)”內(nèi)部的領(lǐng)域服務(wù)與領(lǐng)域模型對象關(guān)聯(lián),顯性地表達每個領(lǐng)域模型的具體工作職責(zé)及業(yè)務(wù)行為事件與領(lǐng)域?qū)ο笾g的上下文映射關(guān)系,如下圖所示。
06 架構(gòu)設(shè)計
架構(gòu)設(shè)計的本質(zhì)是管理業(yè)務(wù)和技術(shù)復(fù)雜性,使系統(tǒng)易于有序化重構(gòu)及擴展。高質(zhì)量的架構(gòu)一定是高度抽象的、圍繞業(yè)務(wù)的、易于理解的、面向演進的。
分層架構(gòu)設(shè)計
領(lǐng)域驅(qū)動設(shè)計遵循“關(guān)注點分離”原則,將技術(shù)實現(xiàn)邏輯封裝在基礎(chǔ)設(shè)施層;將業(yè)務(wù)邏輯封裝在領(lǐng)域?qū)樱M量使領(lǐng)域?qū)哟a與其他層技術(shù)細節(jié)分割開來;將應(yīng)用層作為黏合劑,實現(xiàn)前兩者的協(xié)作;同時UI層可以基于Swagger技術(shù)暴露REST API。分層架構(gòu)如下圖所示。
六邊形(Hexagonal)架構(gòu)模式
六邊形架構(gòu)模式又稱為“端口-適配器”模式,它將系統(tǒng)分為內(nèi)部和外部。內(nèi)部代表應(yīng)用的業(yè)務(wù)邏輯,外部代表應(yīng)用的驅(qū)動邏輯、基礎(chǔ)設(shè)施或其他應(yīng)用。內(nèi)部以API接口呈現(xiàn),通過端口和外部系統(tǒng)通信。外部系統(tǒng)需要使用不同的適配器,適配器負責(zé)對協(xié)議進行轉(zhuǎn)換。應(yīng)用程序能夠以一致的方式與實際運行的設(shè)備和數(shù)據(jù)庫相隔離,方便開發(fā)和測試,六邊形架構(gòu)模式如下圖所示。
微服務(wù)架構(gòu)模式
微服務(wù)架構(gòu)是強調(diào)細粒度、單一職責(zé)的架構(gòu)模式。微服務(wù)架構(gòu)更關(guān)注的是系統(tǒng)的非功能需求:質(zhì)量屬性、演進能力、擴展性、觀測性、軟件交付效率等。微服務(wù)使用CQRS(命令/查詢職責(zé)分離)中的事務(wù)腳本模式應(yīng)對查詢場景,而對于復(fù)雜的業(yè)務(wù)邏輯場景,使用領(lǐng)域驅(qū)動設(shè)計模式。微服務(wù)架構(gòu)模式如下圖所示。
▼
本文內(nèi)容摘自 《微服務(wù)架構(gòu)深度解析:原理、實踐與進階》 一書,想了解更多微服務(wù)架構(gòu)內(nèi)容,歡迎閱讀此書!
本書從微服務(wù)架構(gòu)的設(shè)計理念和方法論切入,從不同角度全面介紹微服務(wù)特性、使用場景、組織流程、構(gòu)建交互、部署交付等軟件工程各個關(guān)鍵環(huán)節(jié)和核心要素,既包含了具體微服務(wù)技術(shù)的源碼解讀、原理分析,也加入了作者在電信、金融領(lǐng)域積累的真實案例和實踐經(jīng)驗。
往期推薦
關(guān)注我回復(fù)「加群」,加入Spring技術(shù)交流群




















