在應(yīng)用架構(gòu)的設(shè)計中,,領(lǐng)域驅(qū)動設(shè)計( DDD為我們提供了一種架構(gòu)設(shè)計方法,既面向業(yè)務(wù),,又面向技術(shù),,從業(yè)務(wù)需求到領(lǐng)域建模,從領(lǐng)域服務(wù)到技術(shù)轉(zhuǎn)化,,強調(diào)開發(fā)人員與領(lǐng)域?qū)<覅f(xié)同,。 DDD的核心思想是業(yè)務(wù)與技術(shù)相結(jié)合的一種過程,,既強調(diào)業(yè)務(wù)的理解,,又強調(diào)應(yīng)用領(lǐng)域建模方法的使用。 DDD的價值DDD對應(yīng)用架構(gòu)設(shè)計有非常大的指導(dǎo)作用,,具體如下所示,。
當然DDD也不是萬能的,,在采用DDD之前,我們需要考慮是否真正需要,,思考以下幾個問題可以幫助我們做出判斷,。
如果以上問題的答案基本都是“是”,,說明系統(tǒng)并沒有復(fù)雜的業(yè)務(wù)邏輯,則可以用一般的面向數(shù)據(jù)的架構(gòu)或者事務(wù)腳本等模式,。但如果業(yè)務(wù)邏輯復(fù)雜,、變化頻繁、團隊對該領(lǐng)域還缺乏一定的認知,,需要進行領(lǐng)域模型和服務(wù)的梳理,,那么DDD會幫助我們抽象和解決問題。 DDD的設(shè)計理念DDD大體的分析過程中,,其中比較關(guān)鍵的幾個切入點是通用語言,、領(lǐng)域、限界上下文,。
通用語言(Ubiquitous Language)業(yè)務(wù)人員和技術(shù)人員在協(xié)作過程中,,如何講同一種語言?在DDD中用通用語言來解決,。通用語言是理解業(yè)務(wù)需求和梳理領(lǐng)域知識的過程,,也是團隊中各個角色就系統(tǒng)目標、范圍與具體功能達成一致的過程,。通用語言可以定義公共術(shù)語,,減少概念混淆,消除歧義和理解偏差,,提升需求和知識消化的效率,,達到概念和代碼的統(tǒng)一,使得虛擬概念和具體實現(xiàn)一致,。 通用語言可能由團隊所有相關(guān)角色參加,,如業(yè)務(wù)代表、產(chǎn)品經(jīng)理,、業(yè)務(wù)架構(gòu)師,、技術(shù)架構(gòu)師、開發(fā)人員。同時,,領(lǐng)域?qū)<乙卜浅jP(guān)鍵,,領(lǐng)域?qū)<倚枰獙I(yè)務(wù)領(lǐng)域非常了解,或者能夠跟領(lǐng)域?qū)I(yè)人員學(xué)習(xí)到足夠的領(lǐng)域知識,。 通用語言建立的過程并不容易,,因為技術(shù)人員和領(lǐng)域?qū)<以跍贤ㄟ^程中存在“天然屏障”:
因此,,在建立領(lǐng)域知識的時候,,雙方必須交換知識,彼此深度參與,,才可能得出領(lǐng)域模型,。知識的范圍涉及領(lǐng)域模型的各個元素,,如果一方感到困惑,,那么應(yīng)該立刻換一種方式,直到雙方能夠理解一致,。 領(lǐng)域(Domain)領(lǐng)域是用于確定范圍和邊界的,,DDD將業(yè)務(wù)上的問題限定歸屬在特定的邊界內(nèi),而這些邊界就可以叫作領(lǐng)域,。為了降低業(yè)務(wù)理解和系統(tǒng)實現(xiàn)的復(fù)雜度,,DDD會將領(lǐng)域進一步劃分為更細粒度,也就是子域,。子域根據(jù)自身的重要程度和功能屬性又可以劃分為三類子域,。
領(lǐng)域中的核心是領(lǐng)域模型(Domain Model),領(lǐng)域模型具備自己的屬性行為狀態(tài),,并與現(xiàn)實世界的業(yè)務(wù)對象相映射,,領(lǐng)域模型之間具備明確的職責(zé)劃分,領(lǐng)域?qū)ο笤刂g通過聚合和引用來關(guān)聯(lián)相應(yīng)的業(yè)務(wù)規(guī)則,,同時反映通用語言中的領(lǐng)域知識,。 領(lǐng)域模型通過提煉領(lǐng)域?qū)ο螅x領(lǐng)域?qū)ο笾g的關(guān)系,、屬性和行為,,屬于DDD的核心產(chǎn)物。 限界上下文(Bounded Context)領(lǐng)域幫助我們對系統(tǒng)進行拆分,,而限界上下文幫助回答各領(lǐng)域之間的邊界及它們?nèi)绾谓换ァ?/span>
這個“細胞膜”就是對限界上下文很形象的舉例,。
限界上下文是一個顯式的邊界,,領(lǐng)域模型便存在于這個邊界之內(nèi),,通用語言必須限制在這個限界上下文之中。在微服務(wù)設(shè)計中,,一般一個限界上下文理論上就可以設(shè)計為一個微服務(wù),。限界上下文對應(yīng)用的邊界交互有重要作用,可以幫助我們保持模型的一致性,,避免邊界之外問題的混淆,。這一點很重要,因為在大多數(shù)組織中,某些術(shù)語在不同的業(yè)務(wù)領(lǐng)域或團隊中有不同的含義,。 在限界上下文中,,通過上下文映射圖(Context Map)確立上下文之間的關(guān)系,通過上下游來表達依賴,,最后形成限界上下文如何在應(yīng)用程序中相互配合的全局視圖,。上下文的交互方法有多種,在實際工作中,,目前使用比較廣的是防腐層和統(tǒng)一協(xié)議,。
這里先就領(lǐng)域和限界上下文舉一個簡單的例子,,此處舉一個關(guān)于購物車訂單支付下單的例子,。
購物車進行在線的支付授權(quán),訂單處理下單過程,,并觸發(fā)支付域的付款結(jié)算,。 這里我們簡化整個建模的過程,假設(shè)已經(jīng)抽象出購物車域,、支付域,、訂單域(通常購物車域也可以被包含在訂單域內(nèi)),核心的 領(lǐng)域之間通過限界上下文進行交互,因為購物車域和支付域密切相關(guān),,需要等待支付授權(quán),,我們通過 DDD的核心概念 DDD在構(gòu)建領(lǐng)域模型的過程中,,涉及比較多的概念,。這里著重解讀一下以下幾個。
實體(Entity)實體是一個具有唯一身份標識的對象,,并且可以在相當長的一段時間內(nèi)持續(xù)變化,。 我們可以對實體進行多次修改,一個實體對象可能和它先前的對象大不相同,,但擁有相同的身份標識,,即依然是同一個實體。 對實體而言,,重要的不是其屬性,,而是其延續(xù)性和身份標識。一般實體的唯一標識有兩種生成方式,。 另外,,實體具有可變性,,這里需要引出兩個概念:
值對象(Value Object)值對象是只關(guān)心屬性的對象,并且是一個沒有標識符的對象,。 值對象本質(zhì)上是一個集合,,這個集合中包含若干用于描述目的、具有整體概念和不可修改的屬性,。它可以避免屬性零碎,,使屬性歸類更加清晰,從概念理解上也更加完整,。 值對象在領(lǐng)域模型中是可以被共享的,,它們應(yīng)該是不可變的,當有其他地方需要用到值對象時,,可以將它的副本作為參數(shù)傳遞,。 值對象與實體的區(qū)別在于:
領(lǐng)域服務(wù)(Domain Service)領(lǐng)域中的一些概念不太適合建模為對象,,它們本質(zhì)上是一些操作、一些動作,,代表領(lǐng)域中一個重要的行為,。這些操作或動作往往涉及多個領(lǐng)域?qū)ο?/span>,并且需要協(xié)調(diào)這些領(lǐng)域?qū)ο?/span>共同完成這個操作或動作,,這就是領(lǐng)域服務(wù)。 領(lǐng)域服務(wù)有一個重要的功能就是可以避免領(lǐng)域邏輯泄露到應(yīng)用層,。如果沒有領(lǐng)域服務(wù),,那么應(yīng)用層會直接調(diào)用領(lǐng)域?qū)ο笸瓿杀驹擃I(lǐng)域服務(wù)執(zhí)行的操作。 領(lǐng)域服務(wù)體現(xiàn)的行為一定是不屬于任何實體和值對象的,,但它屬于領(lǐng)域模型的范疇,,同時領(lǐng)域服務(wù)應(yīng)該是無狀態(tài)的,,應(yīng)確保領(lǐng)域服務(wù)和通用語言是一致的。
雖然服務(wù)本身也是對象,,但它沒有屬性,,只有行為,因此說它是無狀態(tài)的,。
聚合(Aggregate)聚合的核心思想是將關(guān)聯(lián)減至最少,,這樣有助于簡化對象之間的遍歷,使用一個抽象來封裝模型中的引用,。 聚合由兩部分組成:
除根實體外的其他對象都有本地標識,但這些標識只有在聚合內(nèi)部才需要加以區(qū)分,,因為外部對象除根實體外看不到其他對象,。 聚合行為被視為一個整體,在每個行為完成時,,必須滿足聚合內(nèi)部所應(yīng)用的固定規(guī)則的要求,,即保證數(shù)據(jù)變化的一致性。 根實體最終檢查固定規(guī)則,,如**刪除操作必須一次刪除聚合邊界之內(nèi)的所有對象**,。
工廠(Factory)當創(chuàng)建一個對象或創(chuàng)建整個聚合時,,如果創(chuàng)建工作很復(fù)雜,或者暴露了過多的內(nèi)部結(jié)構(gòu),,則可以使用工廠來進行封裝,。也就是說,將創(chuàng)建復(fù)雜對象的實例和聚合的職責(zé)轉(zhuǎn)移到一個單獨的對象,,這個對象本身在領(lǐng)域模型中可能沒有職責(zé),,但它也是領(lǐng)域設(shè)計中的一部分。
我們可以把工廠作為一種創(chuàng)建復(fù)雜對象和聚合的實現(xiàn)方式。工廠用來封裝對象創(chuàng)建所必需的知識,,它們對創(chuàng)建聚合很有幫助,。當聚合的根建立時,所有聚合包含的對象將隨之建立,。 資源庫(Repository)在DDD中,,資源庫作為對象的提供方,能夠實現(xiàn)對象的持久化,,解耦領(lǐng)域內(nèi)業(yè)務(wù)邏輯與底層持久化,。 每個聚合類型可以對應(yīng)一個資源庫,過程中需要避免實體和值對象成為單純的充血模型,,我們需要資源庫把ORM框架與領(lǐng)域模型隔離,,以屏蔽數(shù)據(jù)訪問的技術(shù)復(fù)雜度。資源庫可以獲取持久化對象,,使應(yīng)用程序和領(lǐng)域設(shè)計與持久化技術(shù)解耦,,讓我們始終聚焦于模型,并且將所有對象的存儲和訪問操作交給資源庫來完成,。在面向接口和依賴注入機制支持下,,資源庫也容易通過Mock等方式進行測試。
工廠和資源庫之間存在一定的關(guān)系,,它們都能幫助我們管理領(lǐng)域?qū)ο蟮纳芷?/span>,。然而,工廠關(guān)注的是對象的創(chuàng)建,,而資源庫關(guān)心的是已經(jīng)存在的對象,。 當一個新對象被添加到資源庫時,它應(yīng)該是先由工廠創(chuàng)建過的,,再被傳遞到資源庫,,以便更好地保存它。 另外,,資源庫和數(shù)據(jù)訪問對象( 領(lǐng)域事件(Domain Event)領(lǐng)域事件表示領(lǐng)域中所發(fā)生的重要事件,,在事件發(fā)生后通常會導(dǎo)致進一步的業(yè)務(wù)操作,,或者在系統(tǒng)其他地方引起反應(yīng)。 領(lǐng)域事件非常重要,,我們在系統(tǒng)設(shè)計過程中經(jīng)常需要解耦,,技術(shù)人員一般通過MQ方式進行;架構(gòu)人員可能采用事件驅(qū)動架構(gòu)(
小結(jié)
DDD常用的分析方法DDD常用的分析方法主要有用例分析法,、四色建模法和事件風(fēng)暴法,。 用例分析法用例分析是比較通用的領(lǐng)域建模方法,可以在**比較傳統(tǒng)的需求調(diào)研過程中結(jié)合領(lǐng)域模型的設(shè)計思路**進行,,核心是通過業(yè)務(wù)需求,、場景流程等梳理用例,進而規(guī)劃領(lǐng)域模型,。 用例分析的前提是業(yè)務(wù)架構(gòu)的需求輸入,,其中核心是業(yè)務(wù)能力與業(yè)務(wù)流程。
每個用例應(yīng)該面向一個或多個場景,,場景主要說明應(yīng)用是如何和最終用戶互動的,也就是誰可以使用應(yīng)用做什么,,從而獲得一個明確的業(yè)務(wù)目標,。 編寫用例時要避免使用技術(shù)術(shù)語,應(yīng)該使用最終用戶或者領(lǐng)域?qū)<铱梢岳斫獾恼Z言,,進而我們可以基于用例分析法,,根據(jù)語義來整理用例,然后整理領(lǐng)域模型,,大概步驟如下,。
四色建模法四色建模法在實踐中也比較常用,,其包括以下幾個核心概念。
簡單地說,,四色建模法關(guān)注的是,某個人(
事件風(fēng)暴法事件風(fēng)暴又稱事件建模,與頭腦風(fēng)暴類似,,可以快速分析復(fù)雜的業(yè)務(wù)領(lǐng)域,,完成領(lǐng)域建模的目標。 事件風(fēng)暴是事件驅(qū)動設(shè)計的典型代表,,是一種快速,、輕量且未得到充分認可的群體建模技術(shù),它對于加速開發(fā)團隊非常適用,。 事件風(fēng)暴法關(guān)注以下元素,。
簡單理解就是誰在何時基于什么(輸入)做了什么(命令),,產(chǎn)生了什么(輸出),,影響了什么(事件),最后聚合成了什么(領(lǐng)域),。 事件風(fēng)暴催化并加速整個建模過程,,強調(diào)正確的人(業(yè)務(wù)人員、領(lǐng)域?qū)<?、技術(shù)人員,、架構(gòu)師、測試人員等關(guān)鍵角色都要參與其中),、開放空間(有足夠的空間可以將事件流可視化,,讓人們可以交互討論)、即時貼(至少三種顏色),,關(guān)聯(lián)的人充分討論,,集體決策,,從價值角度來審視業(yè)務(wù)流程的合理性。領(lǐng)域事件容易促使業(yè)務(wù)人員和非業(yè)務(wù)人員達成共識,。 下面通過一個電商的例子說明事件風(fēng)暴的主要過程,。
DDD分層架構(gòu)DDD在具體落地實施的過程中,,強調(diào)四層分層結(jié)構(gòu),,將核心概念進行有效的整合,各層的職能定義如下,。
除了比較經(jīng)典的四層分層架構(gòu),,DDD還有一種松散分層架構(gòu),,即端口適配器架構(gòu)。
端口適配器架構(gòu)通過劃分內(nèi)部和外部,,系統(tǒng)由內(nèi)而外圍繞領(lǐng)域模型展開,。領(lǐng)域部分位于最內(nèi)層,應(yīng)用程序包含領(lǐng)域模型和業(yè)務(wù)邏輯,,對于外部而言,,通過各種適配器進行上下文集成,包括數(shù)據(jù)持久化,、第三方數(shù)據(jù)集成,,同時基于依賴注入和Mock機制,適配器完成便捷的替換和模擬,。 不論哪種分層架構(gòu),,都遵循以下幾個通用的DDD分層原則。
DDD與周邊概念的關(guān)系下面我們來看看DDD與一些周邊概念的關(guān)系,。 DDD與數(shù)據(jù)驅(qū)動設(shè)計的關(guān)系DDD給我們帶來的是設(shè)計模式的改變。DDD的設(shè)計模式與傳統(tǒng)的面向數(shù)據(jù)驅(qū)動的開發(fā)模式有明顯的區(qū)別,。
在DDD中,,領(lǐng)域模型和數(shù)據(jù)模型是解耦的,,有時也不是一一對應(yīng)的,因此在應(yīng)用DDD進行設(shè)計時,,一定要擺脫數(shù)據(jù)模型優(yōu)先的束縛,,不要讓領(lǐng)域模型被數(shù)據(jù)模型“綁架”,設(shè)計出合理的領(lǐng)域模型是首要任務(wù),。 DDD與微服務(wù)的關(guān)系微服務(wù)是技術(shù)層面的分布式技術(shù)架構(gòu)模式,,是技術(shù)實現(xiàn)和部署的范疇,,它提倡將應(yīng)用劃分成更細粒度的服務(wù),服務(wù)之間互相協(xié)調(diào),、互相配合,,為用戶提供最終價值。 DDD根據(jù)限界上下文設(shè)計出的領(lǐng)域模型和領(lǐng)域服務(wù),,通過微服務(wù)進行落地,,并結(jié)合微服務(wù)及其他分布式技術(shù)(如DevOps、CI/CD,、秒殺,、全鏈路壓測等),加速系統(tǒng)的落地,。一個域服務(wù)可由一個微服務(wù)來實現(xiàn),,也可根據(jù)DDD領(lǐng)域分析拆分為多個微服務(wù),對外集合成統(tǒng)一的域服務(wù),。 DDD與企業(yè)架構(gòu)的關(guān)系DDD在企業(yè)架構(gòu)中扮演著重要的角色,。DDD不僅是應(yīng)用架構(gòu)中領(lǐng)域建模重要的設(shè)計方法,同時在企業(yè)架構(gòu)中承接業(yè)務(wù)架構(gòu),,并對技術(shù)架構(gòu)及具體落地時微服務(wù)等技術(shù)實現(xiàn)都有著重要的指導(dǎo)作用,。更重要的是,DDD建立了共同語言,,讓企業(yè)的業(yè)務(wù)人員和技術(shù)人員可以高效地溝通,。同時,有些企業(yè)強調(diào)共享能力中心的設(shè)計和沉淀,,DDD可以作為其模型和服務(wù)建設(shè)方法,,結(jié)合其他架構(gòu)服務(wù)設(shè)計模式及相關(guān)的最佳實踐,助力企業(yè)的架構(gòu)設(shè)計與規(guī)劃,。 DDD與開發(fā)實施的關(guān)系DDD帶來了很多好處,,本質(zhì)上是設(shè)計模式的改變,讓領(lǐng)域與數(shù)據(jù)解耦,,從業(yè)務(wù)需求出發(fā),,從領(lǐng)域出發(fā),分析領(lǐng)域內(nèi)模型及它們之間的關(guān)系,,并進行領(lǐng)域建模,,設(shè)計核心業(yè)務(wù)邏輯,進而實現(xiàn)技術(shù)細節(jié),。DDD起到了承前啟后的關(guān)鍵作用,,其不僅將業(yè)務(wù)人員和技術(shù)人員連接起來,還把系統(tǒng)從需求,、設(shè)計,、開發(fā),、部署、運維整個生命周期環(huán)節(jié)有效地串接起來,,DDD更多地從總體和頂層設(shè)計,,從問題域、解決方案域,、業(yè)務(wù)模型角度,,不深度干預(yù)其他環(huán)節(jié)細節(jié),邊界清晰,,關(guān)注點分離,。 |
|