色哟哟视频在线观看-色哟哟视频在线-色哟哟欧美15最新在线-色哟哟免费在线观看-国产l精品国产亚洲区在线观看-国产l精品国产亚洲区久久

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

2萬字帶你入門DDD

jf_ro2CN3Fa ? 來源:程序員鶴涵 ? 2023-06-15 10:10 ? 次閱讀

DDD理論

微服務(wù)和DDD的淵源

軟件架構(gòu)模式的演進(jìn)

fc37a67c-0b1c-11ee-962d-dac502259ad0.jpg

我們先來分析一下軟件架構(gòu)模式演進(jìn)的三個(gè)階段。

第一階段是單機(jī)架構(gòu): 采用面向過程的設(shè)計(jì)方法,系統(tǒng)包括客戶端UI層和數(shù)據(jù)庫兩層,采用C/S架構(gòu)模式,整個(gè)系統(tǒng)圍繞數(shù)據(jù)庫驅(qū)動(dòng)設(shè)計(jì)和開發(fā),并且總是從設(shè)計(jì)數(shù)據(jù)庫和字段開始。

第二階段是集中式架構(gòu): 采用面向?qū)ο蟮脑O(shè)計(jì)方法,系統(tǒng)包括業(yè)務(wù)接入層、業(yè)務(wù)邏輯層和數(shù)據(jù)庫層,采用經(jīng)典的三層架構(gòu),也有部分應(yīng)用采用傳統(tǒng)的SOA架構(gòu)。這種架構(gòu)容易使系統(tǒng)變得臃腫,可擴(kuò)展性和彈性伸縮性差。

第三階段是分布式微服務(wù)架構(gòu): 隨著微服務(wù)架構(gòu)理念的提出,集中式架構(gòu)正向分布式微服務(wù)架構(gòu)演進(jìn)。微服務(wù)架構(gòu)可以很好地實(shí)現(xiàn)應(yīng)用之間的解耦,解決單體應(yīng)用擴(kuò)展性和彈性伸縮能力不足的問題。

在單機(jī)和集中式架構(gòu)時(shí)代,系統(tǒng)分析、設(shè)計(jì)和開發(fā)往往是獨(dú)立、分階段割裂進(jìn)行的。

很容易導(dǎo)致需求、設(shè)計(jì)與代碼實(shí)現(xiàn)的不一致,往往到了軟件上線后,我們才發(fā)現(xiàn)很多功能并不是自己想要的,或者做出來的功能跟自己提出的需求偏差太大

微服務(wù)設(shè)計(jì)和拆分的困境

那進(jìn)入微服務(wù)架構(gòu)時(shí)代以后,微服務(wù)確實(shí)也解決了原來采用集中式架構(gòu)的單體應(yīng)用的很多問題,比如擴(kuò)展性、彈性伸縮能力、小規(guī)模團(tuán)隊(duì)的敏捷開發(fā)等等。

微服務(wù)的粒度應(yīng)該多大呀?微服務(wù)到底應(yīng)該如何拆分和設(shè)計(jì)呢?微服務(wù)的邊界應(yīng)該在哪里?

2004年埃里克·埃文斯(Eric Evans)發(fā)表了《領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)》(Domain-Driven Design –Tackling Complexity in the Heart of Software)這本書,從此領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)(Domain Driven Design,簡稱DDD)誕生。DDD核心思想是通過領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)方法定義領(lǐng)域模型,從而確定業(yè)務(wù)和應(yīng)用邊界,保證業(yè)務(wù)模型與代碼模型的一致性。

但DDD提出后在軟件開發(fā)領(lǐng)域一直都是“雷聲大,雨點(diǎn)小”!直到Martin Fowler提出微服務(wù)架構(gòu),DDD才真正迎來了自己的時(shí)代。

利用DDD設(shè)計(jì)方法來建立領(lǐng)域模型,劃分領(lǐng)域邊界,再根據(jù)這些領(lǐng)域邊界從業(yè)務(wù)視角來劃分微服務(wù)邊界。而按照DDD方法設(shè)計(jì)出的微服務(wù)的業(yè)務(wù)和應(yīng)用邊界都非常合理,可以很好地實(shí)現(xiàn)微服務(wù)內(nèi)部和外部的“高內(nèi)聚、低耦合”。

現(xiàn)在,很多大型互聯(lián)網(wǎng)企業(yè)已經(jīng)將DDD設(shè)計(jì)方法作為微服務(wù)的主流設(shè)計(jì)方法了。DDD也開始真正火爆起來。

為什么DDD適合微服務(wù)

DDD是一種處理 高度復(fù)雜領(lǐng)域的設(shè)計(jì)思想,它試圖分離技術(shù)實(shí)現(xiàn)的復(fù)雜性,并圍繞業(yè)務(wù)概念構(gòu)建領(lǐng)域模型來控制業(yè)務(wù)的復(fù)雜性,以解決軟件難以理解,難以演進(jìn)的問題。DDD不是架構(gòu),而是一種架構(gòu)設(shè)計(jì)方法論,它通過邊界劃分將復(fù)雜業(yè)務(wù)領(lǐng)域簡單化,幫我們設(shè)計(jì)出清晰的領(lǐng)域和應(yīng)用邊界,可以很容易地實(shí)現(xiàn)架構(gòu)演進(jìn)。

DDD包括戰(zhàn)略設(shè)計(jì)和戰(zhàn)術(shù)設(shè)計(jì)兩部分。

戰(zhàn)略設(shè)計(jì):主要從 業(yè)務(wù)視角出發(fā),建立業(yè)務(wù)領(lǐng)域模型,劃分領(lǐng)域邊界,建立通用語言的限界上下文,限界上下文可以作為微服務(wù)設(shè)計(jì)的參考邊界。

戰(zhàn)術(shù)設(shè)計(jì):則從技術(shù)視角出發(fā),側(cè)重于領(lǐng)域模型的技術(shù)實(shí)現(xiàn),完成軟件開發(fā)和落地,包括:聚合根、實(shí)體、值對象、領(lǐng)域服務(wù)、應(yīng)用服務(wù)和資源庫等代碼邏輯的設(shè)計(jì)和實(shí)現(xiàn)。

我們不妨來看看DDD是如何進(jìn)行戰(zhàn)略設(shè)計(jì)的。

DDD戰(zhàn)略設(shè)計(jì)會(huì)建立領(lǐng)域模型,領(lǐng)域模型可以用于指導(dǎo)微服務(wù)的設(shè)計(jì)和拆分。

事件風(fēng)暴 是建立領(lǐng)域模型的主要方法,它是一個(gè)從發(fā)散到收斂的過程。

它通常采用用例分析、場景分析和用戶旅程分析 ,盡可能全面不遺漏地分解業(yè)務(wù)領(lǐng)域,并梳理領(lǐng)域?qū)ο笾g的關(guān)系,這是一個(gè)發(fā)散的過程。事件風(fēng)暴過程會(huì)產(chǎn)生很多的實(shí)體、命令、事件等領(lǐng)域?qū)ο螅覀儗⑦@些領(lǐng)域?qū)ο髲牟煌木S度進(jìn)行聚類,形成如聚合、限界上下文等邊界,建立領(lǐng)域模型,這就是一個(gè)收斂的過程。

fc52b5f2-0b1c-11ee-962d-dac502259ad0.jpg

我們可以用三步來劃定領(lǐng)域模型和微服務(wù)的邊界。

戰(zhàn)略設(shè)計(jì)主要包括三步:

通過事件風(fēng)暴梳理業(yè)務(wù)過程中的要素,確定領(lǐng)域?qū)嶓w等領(lǐng)域?qū)ο蟆?/p>

根據(jù)領(lǐng)域?qū)嶓w之間的業(yè)務(wù)關(guān)聯(lián)性,將業(yè)務(wù)緊密相關(guān)的實(shí)體組合形成聚合。

根據(jù)業(yè)務(wù)和語義邊界等因素,將一個(gè)或多個(gè)聚合劃定在一個(gè)限界上下文內(nèi),形成領(lǐng)域模型。

戰(zhàn)術(shù)設(shè)計(jì)的主要工作是將領(lǐng)域模型中的領(lǐng)域?qū)ο笈c代碼模型中的代碼對象建立映射關(guān)系,并調(diào)整業(yè)務(wù)架構(gòu)和領(lǐng)域模型以響應(yīng)業(yè)務(wù)變化。最終,將系統(tǒng)架構(gòu)與業(yè)務(wù)架構(gòu)綁定,建立新的映射關(guān)系。

DDD與微服務(wù)的關(guān)系

DDD主要關(guān)注:從業(yè)務(wù) 領(lǐng)域視角劃分領(lǐng)域邊界,構(gòu)建通用語言進(jìn)行高效溝通,通過業(yè)務(wù)抽象,建立領(lǐng)域模型,維持業(yè)務(wù)和代碼的邏輯一致性。

微服務(wù)主要關(guān)注:運(yùn)行時(shí)的進(jìn)程間通信、容錯(cuò)和故障隔離,實(shí)現(xiàn)去中心化數(shù)據(jù)管理和去中心化服務(wù)治理,關(guān)注微服務(wù)的獨(dú)立開發(fā)、測試、構(gòu)建和部署。

總體來說:

DDD是一套完整而系統(tǒng)的設(shè)計(jì)方法,它能帶給你從戰(zhàn)略設(shè)計(jì)到戰(zhàn)術(shù)設(shè)計(jì)的標(biāo)準(zhǔn)設(shè)計(jì)過程,使得你的設(shè)計(jì)思路能夠更加清晰,設(shè)計(jì)過程更加規(guī)范。

DDD善于處理與領(lǐng)域相關(guān)的擁有高復(fù)雜度業(yè)務(wù)的產(chǎn)品開發(fā),通過它可以建立一個(gè)核心而穩(wěn)定的領(lǐng)域模型,有利于領(lǐng)域知識(shí)的傳遞與傳承。

DDD強(qiáng)調(diào)團(tuán)隊(duì)與領(lǐng)域?qū)<业暮献?,能夠幫助你的團(tuán)隊(duì)建立一個(gè)溝通良好的氛圍,構(gòu)建一致的架構(gòu)體系。

DDD的設(shè)計(jì)思想、原則與模式有助于提高你的架構(gòu)設(shè)計(jì)能力。

無論是在新項(xiàng)目中設(shè)計(jì)微服務(wù),還是將系統(tǒng)從單體架構(gòu)演進(jìn)到微服務(wù),都可以遵循DDD的架構(gòu)原則。

DDD不僅適用于微服務(wù),也適用于傳統(tǒng)的單體應(yīng)用 。

關(guān)鍵概念

領(lǐng)域和子域Domain

我們先看一下漢語詞典中對領(lǐng)域的解釋:“領(lǐng)域是從事一種專門活動(dòng)或事業(yè)的范圍 、部類或部門?!卑俣劝倏茖︻I(lǐng)域的解釋:“領(lǐng)域具體指一種特定的范圍 或區(qū)域。”

領(lǐng)域就是用來確定范圍的,范圍即邊界 ,這也是DDD在設(shè)計(jì)中不斷強(qiáng)調(diào)邊界的原因。

既然領(lǐng)域是用來限定業(yè)務(wù)邊界和范圍的,那么就會(huì)有大小之分,領(lǐng)域越大,業(yè)務(wù)范圍就越大,反之則相反。

領(lǐng)域可以進(jìn)一步劃分為子領(lǐng)域。我們把劃分出來的多個(gè)子領(lǐng)域稱為子域,每個(gè)子域?qū)?yīng)一個(gè)更小的問題域或更小的業(yè)務(wù)范圍

fc755e54-0b1c-11ee-962d-dac502259ad0.jpg

我們來看一下上面這張圖。這個(gè)例子是在講如何給桃樹建立一個(gè)完整的生物學(xué)知識(shí)體系。初中生物課其實(shí)早就告訴我們研究方法了。它的研究過程是這樣的。

第一步:確定研究對象,即研究領(lǐng)域 ,這里是一棵桃樹。

第二步:對研究對象進(jìn)行細(xì)分,將桃樹細(xì)分為器官,器官又分為營養(yǎng)器官和生殖器官兩種。其中營養(yǎng)器官包括根、莖和葉,生殖器官包括花、果實(shí)和種子。桃樹的知識(shí)體系是我們已經(jīng)確定要研究的問題域,對應(yīng)DDD的領(lǐng)域。根、莖、葉、花、果實(shí)和種子等器官則是細(xì)分后的問題子域。這個(gè)過程就是DDD將領(lǐng)域細(xì)分為多個(gè)子域 的過程。

第三步:對器官進(jìn)行細(xì)分,將器官細(xì)分為組織。比如,葉子器官可細(xì)分為保護(hù)組織、營養(yǎng)組織和輸導(dǎo)組織等。這個(gè)過程就是DDD將子域進(jìn)一步細(xì)分為多個(gè)子域 的過程。

第四步:對組織進(jìn)行細(xì)分,將組織細(xì)分為細(xì)胞,細(xì)胞成為我們研究的最小單元。細(xì)胞之間的細(xì)胞壁確定了單元的邊界,也確定了研究的最小邊界

第五步:細(xì)胞核、線粒體、細(xì)胞膜等物質(zhì)共同構(gòu)成細(xì)胞,這些物質(zhì)一起協(xié)作讓細(xì)胞具有這類細(xì)胞特定的生物功能。在這里你可以把細(xì)胞理解為DDD的聚合 ,細(xì)胞內(nèi)的這些物質(zhì)就可以理解為聚合里面的聚合根、實(shí)體以及值對象 等,在聚合內(nèi)這些實(shí)體一起協(xié)作完成特定的業(yè)務(wù)功能。這個(gè)過程類似DDD設(shè)計(jì)時(shí),確定微服務(wù)內(nèi)功能要素和邊界的過程。

核心域,通用域和支撐域

在領(lǐng)域不斷劃分的過程中,領(lǐng)域會(huì)細(xì)分為不同的子域,子域可以根據(jù)自身重要性和功能屬性劃分為三類子域,它們分別是:核心域、通用域和支撐域。

決定產(chǎn)品和公司核心競爭力的子域是核心域 ,它是業(yè)務(wù)成功的主要因素和公司的核心競爭力。

沒有太多個(gè)性化的訴求,同時(shí)被多個(gè)子域使用的通用功能子域是通用域

還有一種功能子域是必需的,但既不包含決定產(chǎn)品和公司核心競爭力的功能,也不包含通用功能的子域,它就是支撐域

這三類子域相較之下,核心域是最重要的。

通用域和支撐域如果對應(yīng)到企業(yè)系統(tǒng),舉例來說的話,通用域則是你需要用到的通用系統(tǒng),比如認(rèn)證、權(quán)限、短信等等,這類應(yīng)用很容易買到,沒有企業(yè)特點(diǎn)限制,不需要做太多的定制化。

而支撐域則具有企業(yè)特性,但不具有通用性,例如數(shù)據(jù)代碼類的數(shù)據(jù)字典,站內(nèi)信等系統(tǒng)。

那為什么要?jiǎng)澐趾诵挠?、通用域和支撐域,主要目的是什么呢?/strong>

要事為先

公司在IT系統(tǒng)建設(shè)過程中,由于預(yù)算和資源有限,對不同類型的子域應(yīng)有不同的關(guān)注度和資源投入策略,記住好鋼要用在刀刃上。

很多公司的業(yè)務(wù),表面看上去相似,但商業(yè)模式和戰(zhàn)略方向是存在很大差異的,因此公司的關(guān)注點(diǎn)會(huì)不一樣,在劃分核心域、通用域和支撐域時(shí),其結(jié)果也會(huì)出現(xiàn)非常大的差異。

比如同樣都是電商平臺(tái)的淘寶、天貓、京東和蘇寧易購,他們的商業(yè)模式是不同的。淘寶是C2C網(wǎng)站,個(gè)人賣家對個(gè)人買家,而天貓、京東和蘇寧易購則是B2C網(wǎng)站,是公司賣家對個(gè)人買家。即便是蘇寧易購與京東都是B2C的模式,他們的商業(yè)模式也是不一樣的,蘇寧易購是典型的傳統(tǒng)線下賣場轉(zhuǎn)型成為電商,京東則是直營加部分平臺(tái)模式。

商業(yè)模式的不同會(huì)導(dǎo)致核心域劃分結(jié)果的不同。有的公司核心域可能在客戶服務(wù),有的可能在產(chǎn)品質(zhì)量,有的可能在物流 。在公司領(lǐng)域細(xì)分、建立領(lǐng)域模型和系統(tǒng)建設(shè)時(shí),我們就要結(jié)合公司戰(zhàn)略重點(diǎn)和商業(yè)模式,找到核心域了,且重點(diǎn)關(guān)注核心域。

通用語言

在事件風(fēng)暴過程中,通過團(tuán)隊(duì)交流達(dá)成共識(shí)的,能夠簡單、清晰、準(zhǔn)確描述業(yè)務(wù)涵義和規(guī)則的語言就是通用語言。也就是說,通用語言是團(tuán)隊(duì)統(tǒng)一的語言 ,不管你在團(tuán)隊(duì)中承擔(dān)什么角色,在同一個(gè)領(lǐng)域的軟件生命周期里都使用統(tǒng)一的語言進(jìn)行交流。

通用語言的價(jià)值也就很明了了,它可以解決交流障礙這個(gè)問題 ,使領(lǐng)域?qū)<液烷_發(fā)人員能夠協(xié)同合作,從而確保業(yè)務(wù)需求的正確表達(dá)。

但是,對這個(gè)概念的理解,到這里還不夠。

通用語言包含術(shù)語和用例場景 ,并且能夠直接反映在代碼中。通用語言中的名詞可以給領(lǐng)域?qū)ο竺?/strong> ,如商品、訂單等,對應(yīng)實(shí)體對象;而動(dòng)詞則表示一個(gè)動(dòng)作或事件 ,如商品已下單、訂單已付款等,對應(yīng)領(lǐng)域事件或者命令。

通用語言貫穿DDD的整個(gè)設(shè)計(jì)過程。作為項(xiàng)目團(tuán)隊(duì)溝通和協(xié)商形成的統(tǒng)一語言,基于它,你就能夠開發(fā)出可讀性更好的代碼,將業(yè)務(wù)需求準(zhǔn)確轉(zhuǎn)化為代碼設(shè)計(jì)。

這張圖描述了從事件風(fēng)暴建立通用語言到領(lǐng)域?qū)ο笤O(shè)計(jì)和代碼落地的完整過程。

簡單來說,通用語言確定了項(xiàng)目團(tuán)隊(duì)內(nèi)部交流的統(tǒng)一語言,而這個(gè)語言所在的語義環(huán)境則是由限界上下文來限定的,以確保語義的唯一性。

限界上下文BoundedContext

我們可以將限界上下文拆解為兩個(gè)詞:限界和上下文。限界就是領(lǐng)域的邊界,而上下文則是語義環(huán)境 。通過領(lǐng)域的限界上下文,我們就可以在統(tǒng)一的領(lǐng)域邊界內(nèi)用統(tǒng)一的語言進(jìn)行交流。

綜合一下,我認(rèn)為限界上下文的定義就是:用來封裝通用語言和領(lǐng)域?qū)ο?,提供上下文環(huán)境,保證在領(lǐng)域之內(nèi)的一些術(shù)語、業(yè)務(wù)相關(guān)對象等(通用語言)有一個(gè)確切的含義,沒有二義性。這個(gè)邊界定義了模型的適用范圍,使團(tuán)隊(duì)所有成員能夠明確地知道什么應(yīng)該在模型中實(shí)現(xiàn),什么不應(yīng)該在模型中實(shí)現(xiàn)。

領(lǐng)域?qū)<摇⒓軜?gòu)師和開發(fā)人員的主要工作就是通過事件風(fēng)暴來劃分限界上下文。限界上下文確定了微服務(wù)的設(shè)計(jì)和拆分方向,是微服務(wù)設(shè)計(jì)和拆分的主要依據(jù)。如果不考慮技術(shù)異構(gòu)、團(tuán)隊(duì)溝通等其它外部因素,一個(gè)限界上下文理論上就可以設(shè)計(jì)為一個(gè)微服務(wù)。

實(shí)體Entity

我們先來看一下實(shí)體是什么東西?

在DDD中有這樣一類對象,它們擁有唯一標(biāo)識(shí)符,且標(biāo)識(shí)符在歷經(jīng)各種狀態(tài)變更后仍能保持一致 。對這些對象而言,重要的不是其屬性,而是其延續(xù)性和標(biāo)識(shí),對象的延續(xù)性和標(biāo)識(shí)會(huì)跨越甚至超出軟件的生命周期。我們把這樣的對象稱為實(shí)體。

1. 實(shí)體的業(yè)務(wù)形態(tài)

在DDD不同的設(shè)計(jì)過程中,實(shí)體的形態(tài)是不同的。在戰(zhàn)略設(shè)計(jì)時(shí),實(shí)體是領(lǐng)域模型的一個(gè)重要對象。領(lǐng)域模型中的實(shí)體是多個(gè)屬性、操作或行為的載體。在事件風(fēng)暴中,我們可以根據(jù)命令、操作或者事件,找出產(chǎn)生這些行為的業(yè)務(wù)實(shí)體對象,進(jìn)而按照一定的業(yè)務(wù)規(guī)則將依存度高和業(yè)務(wù)關(guān)聯(lián)緊密的多個(gè)實(shí)體對象和值對象進(jìn)行聚類,形成聚合。你可以這么理解,實(shí)體和值對象是組成領(lǐng)域模型的基礎(chǔ)單元。

2. 實(shí)體的代碼形態(tài)

在代碼模型中,實(shí)體的表現(xiàn)形式是實(shí)體類,這個(gè)類包含了實(shí)體的屬性和方法,通過這些方法實(shí)現(xiàn)實(shí)體自身的業(yè)務(wù)邏輯。在DDD里,這些實(shí)體類通常采用充血模型 ,與這個(gè)實(shí)體相關(guān)的所有業(yè)務(wù)邏輯都在實(shí)體類的方法中實(shí)現(xiàn),跨多個(gè)實(shí)體的領(lǐng)域邏輯則在領(lǐng)域服務(wù)中實(shí)現(xiàn)

3. 實(shí)體的運(yùn)行形態(tài)

實(shí)體以DO(領(lǐng)域?qū)ο螅┑男问酱嬖冢?strong>每個(gè)實(shí)體對象都有唯一的ID 。我們可以對一個(gè)實(shí)體對象進(jìn)行多次修改,修改后的數(shù)據(jù)和原來的數(shù)據(jù)可能會(huì)大不相同。但是,由于它們擁有相同的ID,它們依然是同一個(gè)實(shí)體。比如商品是商品上下文的一個(gè)實(shí)體,通過唯一的商品ID來標(biāo)識(shí),不管這個(gè)商品的數(shù)據(jù)如何變化,商品的ID一直保持不變,它始終是同一個(gè)商品。

4. 實(shí)體的數(shù)據(jù)庫形態(tài)

與傳統(tǒng)數(shù)據(jù)模型設(shè)計(jì)優(yōu)先不同,DDD是先構(gòu)建領(lǐng)域模型 ,針對實(shí)際業(yè)務(wù)場景構(gòu)建實(shí)體對象和行為,再將實(shí)體對象映射到數(shù)據(jù)持久化對象。

在領(lǐng)域模型映射到數(shù)據(jù)模型時(shí),一個(gè)實(shí)體可能對應(yīng)0個(gè)、1個(gè)或者多個(gè)數(shù)據(jù)庫持久化對象。大多數(shù)情況下實(shí)體與持久化對象是一對一 。在某些場景中,有些實(shí)體只是暫駐靜態(tài)內(nèi)存的一個(gè)運(yùn)行態(tài)實(shí)體,它不需要持久化。比如,基于多個(gè)價(jià)格配置數(shù)據(jù)計(jì)算后生成的折扣實(shí)體。

而在有些復(fù)雜場景下,實(shí)體與持久化對象則可能是一對多或者多對一的關(guān)系。比如,用戶user與角色role兩個(gè)持久化對象可生成權(quán)限實(shí)體,一個(gè)實(shí)體對應(yīng)兩個(gè)持久化對象,這是一對多的場景。再比如,有些場景為了避免數(shù)據(jù)庫的聯(lián)表查詢,提升系統(tǒng)性能,會(huì)將客戶信息customer和賬戶信息account兩類數(shù)據(jù)保存到同一張數(shù)據(jù)庫表中,客戶和賬戶兩個(gè)實(shí)體可根據(jù)需要從一個(gè)持久化對象中生成,這就是多對一的場景。

值對象ValueObject

《實(shí)現(xiàn)領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)》一書中對值對象的定義:通過對象屬性值來識(shí)別的對象,它將多個(gè)相關(guān)屬性組合為一個(gè)概念整體。在DDD中用來描述領(lǐng)域的特定方面,并且是一個(gè)沒有標(biāo)識(shí)符的對象,叫作值對象。

值對象描述了領(lǐng)域中的一件東西,這個(gè)東西是不可變的 ,它將不同的相關(guān)屬性組合成了一個(gè)概念整體。當(dāng)度量和描述改變時(shí),可以用另外一個(gè)值對象予以替換。它可以和其它值對象進(jìn)行相等性比較,且不會(huì)對協(xié)作對象造成副作用。這部分在后面講“值對象的運(yùn)行形態(tài)”時(shí)還會(huì)有例子。

簡單來說就是一堆不可變的屬性的集合,為了避免屬性的零碎

fc88a09a-0b1c-11ee-962d-dac502259ad0.jpg

人員實(shí)體原本包括:姓名、年齡、性別以及人員所在的省、市、縣和街道等屬性。這樣顯示地址相關(guān)的屬性就很零碎了對不對?現(xiàn)在,我們可以將“省、市、縣和街道等屬性”拿出來構(gòu)成一個(gè)“地址屬性集合”,這個(gè)集合就是值對象了。

1. 值對象的業(yè)務(wù)形態(tài)

值對象是DDD領(lǐng)域模型中的一個(gè)基礎(chǔ)對象,它跟實(shí)體一樣都來源于事件風(fēng)暴所構(gòu)建的領(lǐng)域模型,都包含了若干個(gè)屬性,它與實(shí)體一起構(gòu)成聚合。

我們不妨對照實(shí)體,來看值對象的業(yè)務(wù)形態(tài),這樣更好理解。本質(zhì)上,實(shí)體是看得到、摸得著的實(shí)實(shí)在在的業(yè)務(wù)對象,實(shí)體具有業(yè)務(wù)屬性、業(yè)務(wù)行為和業(yè)務(wù)邏輯。而值對象只是若干個(gè)屬性的集合 ,只有數(shù)據(jù)初始化操作和有限的不涉及修改數(shù)據(jù)的行為,基本不包含業(yè)務(wù)邏輯。值對象的屬性集雖然在物理上獨(dú)立出來了,但在邏輯上它仍然是實(shí)體屬性的一部分,用于描述實(shí)體的特征。

在值對象中也有部分共享的標(biāo)準(zhǔn)類型的值對象,它們有自己的限界上下文,有自己的持久化對象,可以建立共享的數(shù)據(jù)類微服務(wù),比如數(shù)據(jù)字典。

2. 值對象的代碼形態(tài)

值對象在代碼中有這樣兩種形態(tài)。如果值對象是單一屬性,則直接定義為實(shí)體類的屬性;如果值對象是屬性集合,則把它設(shè)計(jì)為Class類 ,Class將具有整體概念的多個(gè)屬性歸集到屬性集合,這樣的值對象沒有ID,會(huì)被實(shí)體整體引用。

我們看一下下面這段代碼,person這個(gè)實(shí)體有若干個(gè)單一屬性的值對象,比如Id、name等屬性;同時(shí)它也包含多個(gè)屬性的值對象,比如地址address。

fca0e5e2-0b1c-11ee-962d-dac502259ad0.jpg

3. 值對象的運(yùn)行形態(tài)

實(shí)體實(shí)例化后的DO對象的業(yè)務(wù)屬性和業(yè)務(wù)行為非常豐富,但值對象實(shí)例化的對象則相對簡單和乏味。除了值對象數(shù)據(jù)初始化和整體替換的行為外,其它業(yè)務(wù)行為就很少了。

值對象嵌入到實(shí)體的話,有這樣兩種不同的數(shù)據(jù)格式,也可以說是兩種方式,分別是屬性嵌入的方式和序列化大對象的方式。

引用單一屬性的值對象或只有一條記錄的多屬性值對象的實(shí)體,可以采用屬性嵌入的方式嵌入。引用一條或多條記錄的多屬性值對象的實(shí)體,可以采用序列化大對象的方式嵌入。比如,人員實(shí)體可以有多個(gè)通訊地址,多個(gè)地址序列化后可以嵌入人員的地址屬性。值對象創(chuàng)建后就不允許修改了,只能用另外一個(gè)值對象來整體替換。

案例1:以屬性嵌入的方式形成的人員實(shí)體對象,地址值對象直接以屬性值嵌入人員實(shí)體中。

fcc6e738-0b1c-11ee-962d-dac502259ad0.jpg

案例2:以序列化大對象的方式形成的人員實(shí)體對象,地址值對象被序列化成大對象Json串后,嵌入人員實(shí)體中。

fcd51718-0b1c-11ee-962d-dac502259ad0.jpg

4. 值對象的數(shù)據(jù)庫形態(tài)

DDD引入值對象是希望實(shí)現(xiàn)從“數(shù)據(jù)建模為中心”向“領(lǐng)域建模為中心”轉(zhuǎn)變,減少數(shù)據(jù)庫表的數(shù)量和表與表之間復(fù)雜的依賴關(guān)系,盡可能地簡化數(shù)據(jù)庫設(shè)計(jì),提升數(shù)據(jù)庫性能。

如何理解用值對象來簡化數(shù)據(jù)庫設(shè)計(jì)呢?

傳統(tǒng)的數(shù)據(jù)建模大多是根據(jù)數(shù)據(jù)庫范式設(shè)計(jì)的,每一個(gè)數(shù)據(jù)庫表對應(yīng)一個(gè)實(shí)體,每一個(gè)實(shí)體的屬性值用單獨(dú)的一列來存儲(chǔ),一個(gè)實(shí)體主表會(huì)對應(yīng)N個(gè)實(shí)體從表。而值對象在數(shù)據(jù)庫持久化方面簡化了設(shè)計(jì),它的數(shù)據(jù)庫設(shè)計(jì)大多采用非數(shù)據(jù)庫范式,值對象的屬性值和實(shí)體對象的屬性值保存在同一個(gè)數(shù)據(jù)庫實(shí)體表中

舉個(gè)例子,還是基于上述人員和地址那個(gè)場景,實(shí)體和數(shù)據(jù)模型設(shè)計(jì)通常有兩種解決方案:

第一是把地址值對象的所有屬性都放到人員實(shí)體表中,創(chuàng)建人員實(shí)體,創(chuàng)建人員數(shù)據(jù)表;

第二是創(chuàng)建人員和地址兩個(gè)實(shí)體,同時(shí)創(chuàng)建人員和地址兩張表。

第一個(gè)方案會(huì)破壞地址的業(yè)務(wù)涵義和概念完整性,第二個(gè)方案增加了不必要的實(shí)體和表,需要處理多個(gè)實(shí)體和表的關(guān)系,從而增加了數(shù)據(jù)庫設(shè)計(jì)的復(fù)雜性。

那到底應(yīng)該怎樣設(shè)計(jì),才能讓業(yè)務(wù)含義清楚,同時(shí)又不讓數(shù)據(jù)庫變得復(fù)雜呢?

在領(lǐng)域建模時(shí),我們可以將部分對象設(shè)計(jì)為值對象,保留對象的業(yè)務(wù)涵義,同時(shí)又減少了實(shí)體的數(shù)量;在數(shù)據(jù)建模時(shí),我們可以將值對象嵌入實(shí)體,減少實(shí)體表的數(shù)量,簡化數(shù)據(jù)庫設(shè)計(jì)。

要想發(fā)揮對象的威力,就需要優(yōu)先做領(lǐng)域建模,弱化數(shù)據(jù)庫的作用,只把數(shù)據(jù)庫作為一個(gè)保存數(shù)據(jù)的倉庫即可 。即使違反數(shù)據(jù)庫設(shè)計(jì)原則,也不用大驚小怪,只要業(yè)務(wù)能夠順利運(yùn)行,就沒什么關(guān)系。

聚合根AggregateRoot

聚合根的主要目的是為了避免由于復(fù)雜數(shù)據(jù)模型缺少統(tǒng)一的業(yè)務(wù)規(guī)則控制,而導(dǎo)致聚合、實(shí)體之間數(shù)據(jù)不一致性的問題。

如果把聚合比作組織,那聚合根就是這個(gè)組織的負(fù)責(zé)人。聚合根也稱為根實(shí)體,它不僅是實(shí)體,還是聚合的管理者。

首先它作為實(shí)體本身,擁有實(shí)體的屬性和業(yè)務(wù)行為,實(shí)現(xiàn)自身的業(yè)務(wù)邏輯。

其次它作為聚合的管理者,在聚合內(nèi)部負(fù)責(zé)協(xié)調(diào)實(shí)體和值對象按照固定的業(yè)務(wù)規(guī)則協(xié)同完成共同的業(yè)務(wù)邏輯。

最后在聚合之間,它還是聚合對外的接口人,以聚合根ID關(guān)聯(lián)的方式接受外部任務(wù)和請求,在上下文內(nèi)實(shí)現(xiàn)聚合之間的業(yè)務(wù)協(xié)同。也就是說,聚合之間通過聚合根ID關(guān)聯(lián)引用,如果需要訪問其它聚合的實(shí)體,就要先訪問聚合根,再導(dǎo)航到聚合內(nèi)部實(shí)體,外部對象不能直接訪問聚合內(nèi)實(shí)體。

聚合Aggregate

在DDD中,實(shí)體和值對象是很基礎(chǔ)的領(lǐng)域?qū)ο?。?shí)體一般對應(yīng)業(yè)務(wù)對象,它具有業(yè)務(wù)屬性和業(yè)務(wù)行為;而值對象主要是屬性集合,對實(shí)體的狀態(tài)和特征進(jìn)行描述。但實(shí)體和值對象都只是個(gè)體化的對象,它們的行為表現(xiàn)出來的是個(gè)體的能力。

舉個(gè)例子。社會(huì)是由一個(gè)個(gè)的個(gè)體組成的,象征著我們每一個(gè)人。隨著社會(huì)的發(fā)展,慢慢出現(xiàn)了社團(tuán)、機(jī)構(gòu)、部門等組織,我們開始從個(gè)人變成了組織的一員,大家可以協(xié)同一致的工作,朝著一個(gè)最大的目標(biāo)前進(jìn),發(fā)揮出更大的力量。

領(lǐng)域模型內(nèi)的實(shí)體和值對象就好比個(gè)體,而能讓實(shí)體和值對象協(xié)同工作的組織就是聚合,它用來確保這些領(lǐng)域?qū)ο笤趯?shí)現(xiàn)共同的業(yè)務(wù)邏輯時(shí),能保證數(shù)據(jù)的一致性。

聚合就是由業(yè)務(wù)和邏輯緊密關(guān)聯(lián)的實(shí)體和值對象組合而成的 ,聚合是數(shù)據(jù)修改和持久化的基本單元,每一個(gè)聚合對應(yīng)一個(gè)倉儲(chǔ),實(shí)現(xiàn)數(shù)據(jù)的持久化。

聚合有一個(gè)聚合根和上下文邊界,這個(gè)邊界根據(jù)業(yè)務(wù)單一職責(zé)和高內(nèi)聚原則,定義了聚合內(nèi)部應(yīng)該包含哪些實(shí)體和值對象,而聚合之間的邊界是松耦合的。按照這種方式設(shè)計(jì)出來的微服務(wù)很自然就是“高內(nèi)聚、低耦合”的。

聚合在DDD分層架構(gòu)里屬于領(lǐng)域?qū)樱I(lǐng)域?qū)影硕鄠€(gè)聚合,共同實(shí)現(xiàn)核心業(yè)務(wù)邏輯。聚合內(nèi)實(shí)體以充血模型實(shí)現(xiàn)個(gè)體業(yè)務(wù)能力,以及業(yè)務(wù)邏輯的高內(nèi)聚 ??缍鄠€(gè)實(shí)體的業(yè)務(wù)邏輯通過領(lǐng)域服務(wù)來實(shí)現(xiàn),跨多個(gè)聚合的業(yè)務(wù)邏輯通過應(yīng)用服務(wù)來實(shí)現(xiàn)。比如有的業(yè)務(wù)場景需要同一個(gè)聚合的A和B兩個(gè)實(shí)體來共同完成,我們就可以將這段業(yè)務(wù)邏輯用領(lǐng)域服務(wù)來實(shí)現(xiàn);而有的業(yè)務(wù)邏輯需要聚合C和聚合D中的兩個(gè)服務(wù)共同完成,這時(shí)你就可以用應(yīng)用服務(wù)來組合這兩個(gè)服務(wù)。

怎樣設(shè)計(jì)聚合?

DDD領(lǐng)域建模通常采用事件風(fēng)暴,它通常采用用例分析、場景分析和用戶旅程 分析等方法,通過頭腦風(fēng)暴列出所有可能的業(yè)務(wù)行為和事件,然后找出產(chǎn)生這些行為的領(lǐng)域?qū)ο?,并梳理領(lǐng)域?qū)ο笾g的關(guān)系,找出聚合根,找出與聚合根業(yè)務(wù)緊密關(guān)聯(lián)的實(shí)體和值對象,再將聚合根、實(shí)體和值對象組合,構(gòu)建聚合。

下面我們以保險(xiǎn)的投保業(yè)務(wù)場景為例,看一下聚合的構(gòu)建過程主要都包括哪些步驟。

fcec5d4c-0b1c-11ee-962d-dac502259ad0.png

第 1 步: 采用事件風(fēng)暴,根據(jù)業(yè)務(wù)行為,梳理出在投保過程中發(fā)生這些行為的所有的實(shí)體和值對象 ,比如投保單、標(biāo)的、客戶、被保人等等。

第 2 步: 從眾多實(shí)體中選出適合作為對象管理者的根實(shí)體,也就是聚合根。判斷一個(gè)實(shí)體是否是聚合根,你可以結(jié)合以下場景分析:是否有獨(dú)立的生命周期?是否有全局唯一ID?是否可以創(chuàng)建或修改其它對象?是否有專門的模塊來管這個(gè)實(shí)體。圖中的聚合根分別是投保單和客戶實(shí)體。

第 3 步: 根據(jù)業(yè)務(wù)單一職責(zé)和高內(nèi)聚原則,找出與聚合根關(guān)聯(lián)的所有緊密依賴的實(shí)體和值對象。構(gòu)建出 1 個(gè)包含聚合根(唯一)、多個(gè)實(shí)體和值對象的對象集合,這個(gè)集合就是聚合。在圖中我們構(gòu)建了客戶和投保這兩個(gè)聚合。

第 4 步: 在聚合內(nèi)根據(jù)聚合根、實(shí)體和值對象的依賴關(guān)系,畫出對象的引用和依賴模型。這里我需要說明一下:投保人和被保人的數(shù)據(jù),是通過關(guān)聯(lián)客戶ID從客戶聚合中獲取的,在投保聚合里它們是投保單的值對象,這些值對象的數(shù)據(jù)是客戶的冗余數(shù)據(jù),即使未來客戶聚合的數(shù)據(jù)發(fā)生了變更,也不會(huì)影響投保單的值對象數(shù)據(jù)。從圖中我們還可以看出實(shí)體之間的引用關(guān)系,比如在投保聚合里投保單聚合根引用了報(bào)價(jià)單實(shí)體,報(bào)價(jià)單實(shí)體則引用了報(bào)價(jià)規(guī)則子實(shí)體。

第 5 步: 多個(gè)聚合根據(jù)業(yè)務(wù)語義和上下文一起劃分到同一個(gè)限界上下文內(nèi)。

這就是一個(gè)聚合誕生的完整過程了。

聚合的一些設(shè)計(jì)原則

《實(shí)現(xiàn)領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)》一書中對聚合設(shè)計(jì)原則的描述

1. 在一致性邊界內(nèi)建模真正的不變條件。 聚合用來封裝真正的不變性,而不是簡單地將對象組合在一起。聚合內(nèi)有一套不變的業(yè)務(wù)規(guī)則,各實(shí)體和值對象按照統(tǒng)一的業(yè)務(wù)規(guī)則運(yùn)行,實(shí)現(xiàn)對象數(shù)據(jù)的一致性,邊界之外的任何東西都與該聚合無關(guān),這就是聚合能實(shí)現(xiàn)業(yè)務(wù)高內(nèi)聚的原因。

2. 設(shè)計(jì)小聚合。 如果聚合設(shè)計(jì)得過大,聚合會(huì)因?yàn)榘^多的實(shí)體,導(dǎo)致實(shí)體之間的管理過于復(fù)雜,高頻操作時(shí)會(huì)出現(xiàn)并發(fā)沖突或者數(shù)據(jù)庫鎖,最終導(dǎo)致系統(tǒng)可用性變差。而小聚合設(shè)計(jì)則可以降低由于業(yè)務(wù)過大導(dǎo)致聚合重構(gòu)的可能性,讓領(lǐng)域模型更能適應(yīng)業(yè)務(wù)的變化。

3. 通過唯一標(biāo)識(shí)引用其它聚合。 聚合之間是通過關(guān)聯(lián)外部聚合根ID的方式引用,而不是直接對象引用的方式。外部聚合的對象放在聚合邊界內(nèi)管理,容易導(dǎo)致聚合的邊界不清晰,也會(huì)增加聚合之間的耦合度。

4. 在邊界之外使用最終一致性。 聚合內(nèi)數(shù)據(jù)強(qiáng)一致性,而聚合之間數(shù)據(jù)最終一致性。在一次事務(wù)中,最多只能更改一個(gè)聚合的狀態(tài)。如果一次業(yè)務(wù)操作涉及多個(gè)聚合狀態(tài)的更改,應(yīng)采用領(lǐng)域事件的方式異步修改相關(guān)的聚合,實(shí)現(xiàn)聚合之間的解耦。

5. 通過應(yīng)用層實(shí)現(xiàn)跨聚合的服務(wù)調(diào)用。 為實(shí)現(xiàn)微服務(wù)內(nèi)聚合之間的解耦,以及未來以聚合為單位的微服務(wù)組合和拆分,應(yīng)避免跨聚合的領(lǐng)域服務(wù)調(diào)用和跨聚合的數(shù)據(jù)庫表關(guān)聯(lián)。

原則需要消化吸收后靈活運(yùn)用到自己的系統(tǒng)中才能產(chǎn)生威力

領(lǐng)域事件

領(lǐng)域事件是領(lǐng)域模型中非常重要的一部分,用來表示領(lǐng)域中發(fā)生的事件。一個(gè)領(lǐng)域事件將導(dǎo)致進(jìn)一步的業(yè)務(wù)操作,在實(shí)現(xiàn)業(yè)務(wù)解耦的同時(shí),還有助于形成完整的業(yè)務(wù)閉環(huán)。

舉例來說的話

領(lǐng)域事件可以是業(yè)務(wù)流程的一個(gè)步驟,比如投保業(yè)務(wù)繳費(fèi)完成后,觸發(fā)投保單轉(zhuǎn)保單的動(dòng)作;

也可能是定時(shí)批處理過程中發(fā)生的事件,比如批處理生成季繳保費(fèi)通知單,觸發(fā)發(fā)送繳費(fèi)郵件通知操作;

或者一個(gè)事件發(fā)生后觸發(fā)的后續(xù)動(dòng)作,比如密碼連續(xù)輸錯(cuò)三次,觸發(fā)鎖定賬戶的動(dòng)作。

那如何識(shí)別領(lǐng)域事件呢?

很簡單,和剛才講的定義是強(qiáng)關(guān)聯(lián)的。在做用戶旅程或者場景分析時(shí),我們要捕捉業(yè)務(wù)、需求人員或領(lǐng)域?qū)<铱谥械年P(guān)鍵詞:“如果發(fā)生……,則……”“當(dāng)做完……的時(shí)候,請通知……”“發(fā)生……時(shí),則……”等。在這些場景中,如果發(fā)生某種事件后,會(huì)觸發(fā)進(jìn)一步的操作,那么這個(gè)事件很可能就是領(lǐng)域事件

領(lǐng)域事件總體架構(gòu)

領(lǐng)域事件的執(zhí)行需要一系列的組件和技術(shù)來支撐。

領(lǐng)域事件處理包括:事件構(gòu)建和發(fā)布、事件數(shù)據(jù)持久化、事件總線、消息中間件、事件接收和處理等。下面我們逐一講一下。

fd1f1174-0b1c-11ee-962d-dac502259ad0.jpg

1. 事件構(gòu)建和發(fā)布

事件基本屬性至少包括:事件唯一標(biāo)識(shí)、發(fā)生時(shí)間、事件類型和事件源 ,其中事件唯一標(biāo)識(shí)應(yīng)該是全局唯一的,以便事件能夠無歧義地在多個(gè)限界上下文中傳遞。事件基本屬性主要記錄事件自身以及事件發(fā)生背景的數(shù)據(jù)。

另外事件中還有一項(xiàng)更重要,那就是業(yè)務(wù)屬性,用于記錄事件發(fā)生那一刻的業(yè)務(wù)數(shù)據(jù),這些數(shù)據(jù)會(huì)隨事件傳輸?shù)接嗛喎?,以開展下一步的業(yè)務(wù)操作。

事件基本屬性和業(yè)務(wù)屬性一起構(gòu)成事件實(shí)體,事件實(shí)體依賴聚合根。領(lǐng)域事件發(fā)生后,事件中的業(yè)務(wù)數(shù)據(jù)不再修改,因此業(yè)務(wù)數(shù)據(jù)可以以序列化值對象的形式保存,這種存儲(chǔ)格式在消息中間件中也比較容易解析和獲取。

為了保證事件結(jié)構(gòu)的統(tǒng)一,我們還會(huì)創(chuàng)建事件基類 DomainEvent(參考下圖),子類可以擴(kuò)充屬性和方法。由于事件沒有太多的業(yè)務(wù)行為,實(shí)現(xiàn)方法一般比較簡單。

fd37bb7a-0b1c-11ee-962d-dac502259ad0.jpg

事件發(fā)布之前需要先構(gòu)建事件實(shí)體并持久化。事件發(fā)布的方式有很多種,你可以通過應(yīng)用服務(wù)或者領(lǐng)域服務(wù)發(fā)布到事件總線或者消息中間件,也可以從事件表中利用定時(shí)程序或數(shù)據(jù)庫日志捕獲技術(shù)獲取增量事件數(shù)據(jù),發(fā)布到消息中間件。

2. 事件數(shù)據(jù)持久化

事件數(shù)據(jù)持久化可用于系統(tǒng)之間的數(shù)據(jù)對賬,或者實(shí)現(xiàn)發(fā)布方和訂閱方事件數(shù)據(jù)的審計(jì)。當(dāng)遇到消息中間件、訂閱方系統(tǒng)宕機(jī)或者網(wǎng)絡(luò)中斷,在問題解決后仍可繼續(xù)后續(xù)業(yè)務(wù)流轉(zhuǎn),保證數(shù)據(jù)的一致性。

事件數(shù)據(jù)持久化有兩種方案,在實(shí)施過程中你可以根據(jù)自己的業(yè)務(wù)場景進(jìn)行選擇。

持久化到本地業(yè)務(wù)數(shù)據(jù)庫的事件表中,利用本地事務(wù)保證業(yè)務(wù)和事件數(shù)據(jù)的一致性。

持久化到共享的事件數(shù)據(jù)庫中。這里需要注意的是:業(yè)務(wù)數(shù)據(jù)庫和事件數(shù)據(jù)庫不在一個(gè)數(shù)據(jù)庫中,它們的數(shù)據(jù)持久化操作會(huì)跨數(shù)據(jù)庫,因此需要分布式事務(wù)機(jī)制來保證業(yè)務(wù)和事件數(shù)據(jù)的強(qiáng)一致性,結(jié)果就是會(huì)對系統(tǒng)性能造成一定的影響。

3. 事件總線(EventBus)

事件總線是實(shí)現(xiàn)微服務(wù)內(nèi)聚合之間領(lǐng)域事件的重要組件,它提供事件分發(fā)和接收等服務(wù)。事件總線是進(jìn)程內(nèi)模型,它會(huì)在微服務(wù)內(nèi)聚合之間遍歷訂閱者列表,采取同步或異步的模式傳遞數(shù)據(jù)。事件分發(fā)流程大致如下:

如果是微服務(wù)內(nèi)的訂閱者(其它聚合),則直接分發(fā)到指定訂閱者;

如果是微服務(wù)外的訂閱者,將事件數(shù)據(jù)保存到事件庫(表)并異步發(fā)送到消息中間件;

如果同時(shí)存在微服務(wù)內(nèi)和外訂閱者,則先分發(fā)到內(nèi)部訂閱者,將事件消息保存到事件庫(表),再異步發(fā)送到消息中間件。

4. 消息中間件

跨微服務(wù)的領(lǐng)域事件大多會(huì)用到消息中間件,實(shí)現(xiàn)跨微服務(wù)的事件發(fā)布和訂閱。消息中間件的產(chǎn)品非常成熟,市場上可選的技術(shù)也非常多,比如Kafka,RabbitMQ等。

5. 事件接收和處理

微服務(wù)訂閱方在應(yīng)用層采用監(jiān)聽機(jī)制,接收消息隊(duì)列中的事件數(shù)據(jù),完成事件數(shù)據(jù)的持久化后,就可以開始進(jìn)一步的業(yè)務(wù)處理。領(lǐng)域事件處理可在領(lǐng)域服務(wù)中實(shí)現(xiàn)。

領(lǐng)域事件運(yùn)行機(jī)制案例

承保業(yè)務(wù)流程的繳費(fèi)通知單事件,來給你解釋一下領(lǐng)域事件的運(yùn)行機(jī)制。這個(gè)領(lǐng)域事件發(fā)生在投保和收款微服務(wù)之間。發(fā)生的領(lǐng)域事件是:繳費(fèi)通知單已生成。下一步的業(yè)務(wù)操作是:繳費(fèi)。

fd5b22f4-0b1c-11ee-962d-dac502259ad0.jpg

事件起點(diǎn):出單員生成投保單,核保通過后,發(fā)起生成繳費(fèi)通知單的操作。

1.投保微服務(wù)應(yīng)用服務(wù),調(diào)用聚合中的領(lǐng)域服務(wù)createPaymentNotice和createPaymentNoticeEvent,分別創(chuàng)建繳費(fèi)通知單、繳費(fèi)通知單事件。其中繳費(fèi)通知單事件類PaymentNoticeEvent繼承基類DomainEvent。

2.利用倉儲(chǔ)服務(wù)持久化繳費(fèi)通知單相關(guān)的業(yè)務(wù)和事件數(shù)據(jù)。為了避免分布式事務(wù),這些業(yè)務(wù)和事件數(shù)據(jù)都持久化到本地投保微服務(wù)數(shù)據(jù)庫中。

3.通過數(shù)據(jù)庫日志捕獲技術(shù)或者定時(shí)程序,從數(shù)據(jù)庫事件表中獲取事件增量數(shù)據(jù),發(fā)布到消息中間件。這里說明:事件發(fā)布也可以通過應(yīng)用服務(wù)或者領(lǐng)域服務(wù)完成發(fā)布。

4.收款微服務(wù)在應(yīng)用層從消息中間件訂閱繳費(fèi)通知單事件消息主題,監(jiān)聽并獲取事件數(shù)據(jù)后,應(yīng)用服務(wù)調(diào)用領(lǐng)域?qū)拥念I(lǐng)域服務(wù)將事件數(shù)據(jù)持久化到本地?cái)?shù)據(jù)庫中。

5.收款微服務(wù)調(diào)用領(lǐng)域?qū)拥念I(lǐng)域服務(wù)PayPremium,完成繳費(fèi)。

6.事件結(jié)束。

事件風(fēng)暴

事件風(fēng)暴需要準(zhǔn)備些什么

1. 事件風(fēng)暴的參與者

除了領(lǐng)域?qū)<?對業(yè)務(wù)極其了解的人),事件風(fēng)暴的其他參與者可以是DDD專家、架構(gòu)師、產(chǎn)品經(jīng)理、項(xiàng)目經(jīng)理、開發(fā)人員和測試人員等項(xiàng)目團(tuán)隊(duì)成員。

領(lǐng)域建模是統(tǒng)一團(tuán)隊(duì)語言的過程,因此項(xiàng)目團(tuán)隊(duì)?wèi)?yīng)盡早地參與到領(lǐng)域建模中,這樣才能高效建立起團(tuán)隊(duì)的通用語言。到了微服務(wù)建設(shè)時(shí),領(lǐng)域模型也更容易和系統(tǒng)架構(gòu)保持一致。

2. 事件風(fēng)暴要準(zhǔn)備的材料

在這個(gè)過程中,我們要用不同顏色的貼紙區(qū)分領(lǐng)域行為。如下圖,

用藍(lán)色表示命令

用綠色表示實(shí)體

橙色表示領(lǐng)域事件

黃色表示補(bǔ)充信息(補(bǔ)充信息主要用來說明注意事項(xiàng),比如外部依賴等。)

fd90d7c8-0b1c-11ee-962d-dac502259ad0.jpg

3. 事件風(fēng)暴的場地

事件風(fēng)暴的發(fā)明者曾經(jīng)建議要準(zhǔn)備八米長的墻,這樣設(shè)計(jì)就不會(huì)受到空間的限制了。當(dāng)然,這個(gè)不是必要條件,看各自的現(xiàn)實(shí)條件吧,不要讓思維受限就好。

或者是一個(gè)多人在線協(xié)作的白板。

4. 事件風(fēng)暴分析的關(guān)注點(diǎn)

在領(lǐng)域建模的過程中,我們需要重點(diǎn)關(guān)注這類業(yè)務(wù)的語言和行為。比如某些業(yè)務(wù)動(dòng)作或行為(事件)是否會(huì)觸發(fā)下一個(gè)業(yè)務(wù)動(dòng)作,這個(gè)動(dòng)作(事件)的輸入和輸出是什么?是誰(實(shí)體)發(fā)出的什么動(dòng)作(命令),觸發(fā)了這個(gè)動(dòng)作(事件)…我們可以從這些暗藏的詞匯中,分析出領(lǐng)域模型中的事件、命令和實(shí)體等領(lǐng)域?qū)ο蟆?/p>

如何用事件風(fēng)暴構(gòu)建領(lǐng)域模型

領(lǐng)域建模的過程主要包括產(chǎn)品愿景、業(yè)務(wù)場景分析、領(lǐng)域建模和微服務(wù)拆分與設(shè)計(jì)這幾個(gè)重要階段。下面以用戶中臺(tái)為例,介紹一下如何用事件風(fēng)暴構(gòu)建領(lǐng)域模型。

1. 產(chǎn)品愿景

產(chǎn)品愿景的主要目的是對產(chǎn)品頂層價(jià)值的設(shè)計(jì),使產(chǎn)品目標(biāo)用戶、核心價(jià)值、差異化競爭點(diǎn)等信息達(dá)成一致,避免產(chǎn)品偏離方向。

產(chǎn)品愿景的參與角色:領(lǐng)域?qū)<?、業(yè)務(wù)需求方、產(chǎn)品經(jīng)理、項(xiàng)目經(jīng)理和開發(fā)經(jīng)理。

在建模之前,項(xiàng)目團(tuán)隊(duì)要思考這樣兩點(diǎn):

用戶中臺(tái)到底能夠做什么?

它的業(yè)務(wù)范圍、目標(biāo)用戶、核心價(jià)值和愿景,與其它同類產(chǎn)品的差異和優(yōu)勢在哪里?

這個(gè)過程也是明確用戶中臺(tái)建設(shè)方向和統(tǒng)一團(tuán)隊(duì)思想的過程。參與者要對每一個(gè)點(diǎn)(下圖最左側(cè)列的內(nèi)容)發(fā)表意見,用水筆寫在貼紙上,貼在黃色貼紙的位置。這個(gè)過程會(huì)讓參與者充分發(fā)表意見,最后會(huì)將發(fā)散的意見統(tǒng)一為通用語言。如果你的團(tuán)隊(duì)的產(chǎn)品愿景和目標(biāo)已經(jīng)很清晰了,那這個(gè)步驟你可以忽略。

2. 業(yè)務(wù)場景分析

場景分析是從用戶視角出發(fā)的,根據(jù)業(yè)務(wù)流程或用戶旅程,采用用例和場景分析 ,探索領(lǐng)域中的典型場景,找出領(lǐng)域事件、實(shí)體和命令等領(lǐng)域?qū)ο螅晤I(lǐng)域建模。事件風(fēng)暴參與者要盡可能地遍歷所有業(yè)務(wù)細(xì)節(jié),充分發(fā)表意見,不要遺漏業(yè)務(wù)要點(diǎn)。

場景分析的參與角色:領(lǐng)域?qū)<?、產(chǎn)品經(jīng)理、需求分析人員、架構(gòu)師、項(xiàng)目經(jīng)理、開發(fā)經(jīng)理和測試經(jīng)理。

用戶中臺(tái)有這樣三個(gè)典型的業(yè)務(wù)場景:

第一個(gè)是系統(tǒng)和崗位設(shè)置,設(shè)置系統(tǒng)中崗位的菜單權(quán)限;

第二個(gè)是用戶權(quán)限配置,為用戶建立賬戶和密碼,設(shè)置用戶崗位;

第三個(gè)是用戶登錄系統(tǒng)和權(quán)限校驗(yàn),生成用戶登錄和操作日志。

我們可以按照業(yè)務(wù)流程,一步一步搜尋用戶業(yè)務(wù)流程中的關(guān)鍵領(lǐng)域事件,比如崗位已創(chuàng)建,用戶已創(chuàng)建等事件。再找出什么行為會(huì)引起這些領(lǐng)域事件,這些行為可能是一個(gè)或若干個(gè)命令組合在一起產(chǎn)生的,比如創(chuàng)建用戶時(shí),第一個(gè)命令是從公司HR系統(tǒng)中獲取用戶信息,第二個(gè)命令是根據(jù)HR的員工信息在用戶中臺(tái)創(chuàng)建用戶,創(chuàng)建完用戶后就會(huì)產(chǎn)生用戶已創(chuàng)建的領(lǐng)域事件。當(dāng)然這個(gè)領(lǐng)域事件可能會(huì)觸發(fā)下一步的操作,比如發(fā)布到郵件系統(tǒng)通知用戶已創(chuàng)建,但也可能到此就結(jié)束了,你需要根據(jù)具體情況來分析是否還有下一步的操作。

fda2442c-0b1c-11ee-962d-dac502259ad0.jpg

3. 領(lǐng)域建模

領(lǐng)域建模時(shí),我們會(huì)根據(jù)場景分析過程中產(chǎn)生的領(lǐng)域?qū)ο螅热缑?、事件等之間關(guān)系,找出產(chǎn)生命令的實(shí)體,分析實(shí)體之間的依賴關(guān)系組成聚合,為聚合劃定限界上下文,建立領(lǐng)域模型以及模型之間的依賴。領(lǐng)域模型利用限界上下文向上可以指導(dǎo)微服務(wù)設(shè)計(jì),通過聚合向下可以指導(dǎo)聚合根、實(shí)體和值對象的設(shè)計(jì)。

領(lǐng)域建模的參與角色:領(lǐng)域?qū)<摇a(chǎn)品經(jīng)理、需求分析人員、架構(gòu)師、項(xiàng)目經(jīng)理、開發(fā)經(jīng)理和測試經(jīng)理。

具體可以分為這樣三步。

第一步:從命令和事件中提取產(chǎn)生這些行為的實(shí)體。用綠色貼紙表示實(shí)體。通過分析用戶中臺(tái)的命令和事件等行為數(shù)據(jù),提取了產(chǎn)生這些行為的用戶、賬戶、認(rèn)證票據(jù)、系統(tǒng)、菜單、崗位和用戶日志七個(gè)實(shí)體。

fdca69de-0b1c-11ee-962d-dac502259ad0.jpg

第二步:根據(jù)聚合根的管理性質(zhì)從七個(gè)實(shí)體中找出聚合根,比如,用戶管理用戶相關(guān)實(shí)體以及值對象,系統(tǒng)可以管理與系統(tǒng)相關(guān)的菜單等實(shí)體等,可以找出用戶和系統(tǒng)等聚合根。然后根據(jù)業(yè)務(wù)依賴和業(yè)務(wù)內(nèi)聚原則,將聚合根以及它關(guān)聯(lián)的實(shí)體和值對象組合為聚合,比如系統(tǒng)和菜單實(shí)體可以組合為“系統(tǒng)功能”聚合。按照上述方法,用戶中臺(tái)就有了系統(tǒng)功能、崗位、用戶信息、用戶日志、賬戶和認(rèn)證票據(jù)六個(gè)聚合。

第三步:劃定限界上下文,根據(jù)上下文語義將聚合歸類。根據(jù)用戶域的上下文語境,用戶基本信息和用戶日志信息這兩個(gè)聚合共同構(gòu)成用戶信息域,分別管理用戶基本信息、用戶登錄和操作日志。認(rèn)證票據(jù)和賬戶這兩個(gè)聚合共同構(gòu)成認(rèn)證域,分別實(shí)現(xiàn)不同方式的登錄和認(rèn)證。系統(tǒng)功能和崗位這兩個(gè)聚合共同構(gòu)成權(quán)限域,分別實(shí)現(xiàn)系統(tǒng)和菜單管理以及系統(tǒng)的崗位配置。根據(jù)業(yè)務(wù)邊界,我們可以將用戶中臺(tái)劃分為三個(gè)限界上下文:用戶信息、認(rèn)證和權(quán)限。

fde4bfd2-0b1c-11ee-962d-dac502259ad0.jpg

到這里我們就完成了用戶中臺(tái)領(lǐng)域模型的構(gòu)建了。那由于領(lǐng)域建模的過程中產(chǎn)生的領(lǐng)域?qū)ο髮?shí)在太多了,我們可以借助表格來記錄。

fe005490-0b1c-11ee-962d-dac502259ad0.jpg

4. 微服務(wù)拆分與設(shè)計(jì)

我們在基礎(chǔ)篇講過,原則上一個(gè)領(lǐng)域模型就可以設(shè)計(jì)為一個(gè)微服務(wù),但由于領(lǐng)域建模時(shí)只考慮了業(yè)務(wù)因素,沒有考慮微服務(wù)落地時(shí)的技術(shù)、團(tuán)隊(duì)以及運(yùn)行環(huán)境等非業(yè)務(wù)因素,因此在微服務(wù)拆分與設(shè)計(jì)時(shí),我們不能簡單地將領(lǐng)域模型作為拆分微服務(wù)的唯一標(biāo)準(zhǔn),它只能作為微服務(wù)拆分的一個(gè)重要依據(jù)。

一般來說一個(gè)限界上下文拆分成一個(gè)微服務(wù)

微服務(wù)的設(shè)計(jì)還需要考慮服務(wù)的粒度、分層、邊界劃分、依賴關(guān)系和集成關(guān)系。除了考慮業(yè)務(wù)職責(zé)單一外,我們還需要考慮將敏態(tài)與穩(wěn)態(tài)業(yè)務(wù)的分離、非功能性需求(如彈性伸縮要求、安全性等要求)、團(tuán)隊(duì)組織和溝通效率、軟件包大小以及技術(shù)異構(gòu)等非業(yè)務(wù)因素。

微服務(wù)設(shè)計(jì)建議參與的角色:領(lǐng)域?qū)<摇a(chǎn)品經(jīng)理、需求分析人員、架構(gòu)師、項(xiàng)目經(jīng)理、開發(fā)經(jīng)理和測試經(jīng)理。

用戶中臺(tái)微服務(wù)設(shè)計(jì)如果不考慮非業(yè)務(wù)因素,我們完全可以按照領(lǐng)域模型與微服務(wù)一對一的關(guān)系來設(shè)計(jì),將用戶中臺(tái)設(shè)計(jì)為:用戶、認(rèn)證和權(quán)限三個(gè)微服務(wù)。但如果用戶日志數(shù)據(jù)量巨大,大到需要采用大數(shù)據(jù)技術(shù)來實(shí)現(xiàn),這時(shí)用戶信息聚合與用戶日志聚合就會(huì)有技術(shù)異構(gòu)。雖然在領(lǐng)域建模時(shí),我們將他們放在一個(gè)了領(lǐng)域模型內(nèi),但如果考慮技術(shù)異構(gòu),這兩個(gè)聚合就不適合放到同一個(gè)微服務(wù)里了。我們可以以聚合作為拆分單位,將用戶基本信息管理和用戶日志管理拆分為兩個(gè)技術(shù)異構(gòu)的微服務(wù),分別用不同的技術(shù)來實(shí)現(xiàn)它們。

分層架構(gòu)

DDD分層架構(gòu)

DDD的分層架構(gòu)在不斷發(fā)展。最早是傳統(tǒng)的四層架構(gòu);后來四層架構(gòu)有了進(jìn)一步的優(yōu)化,實(shí)現(xiàn)了各層對基礎(chǔ)層的解耦;再后來領(lǐng)域?qū)雍蛻?yīng)用層之間增加了上下文環(huán)境(Context)層,五層架構(gòu)(DCI)就此形成了。

fe0f64c6-0b1c-11ee-962d-dac502259ad0.jpg

我們看一下上面這張圖,在最早的傳統(tǒng)四層架構(gòu)中,基礎(chǔ)層是被其它層依賴的,它位于最核心的位置,那按照分層架構(gòu)的思想,它應(yīng)該就是核心,但實(shí)際上領(lǐng)域?qū)硬攀擒浖暮诵?,所以這種依賴是有問題的。后來我們采用了依賴倒置(Dependency inversion principle,DIP)的設(shè)計(jì),優(yōu)化了傳統(tǒng)的四層架構(gòu),實(shí)現(xiàn)了各層對基礎(chǔ)層的解耦。

我們今天講的DDD分層架構(gòu)就是優(yōu)化后的四層架構(gòu)。在下面這張圖中,從上到下依次是:用戶接口層、應(yīng)用層、領(lǐng)域?qū)雍突A(chǔ)層。那DDD各層的主要職責(zé)是什么呢?下面我來逐一介紹一下。

fe3868f8-0b1c-11ee-962d-dac502259ad0.png

1.用戶接口層

用戶接口層負(fù)責(zé)向用戶顯示信息和解釋用戶指令。這里的用戶可能是:用戶、程序、自動(dòng)化測試和批處理腳本等等。

2.應(yīng)用層

應(yīng)用層是很薄的一層,理論上不應(yīng)該有業(yè)務(wù)規(guī)則或邏輯,主要面向用例和流程相關(guān)的操作。但應(yīng)用層又位于領(lǐng)域?qū)又?,因?yàn)轭I(lǐng)域?qū)影鄠€(gè)聚合,所以它可以協(xié)調(diào)多個(gè)聚合的服務(wù)和領(lǐng)域?qū)ο笸瓿煞?wù)編排和組合,協(xié)作完成業(yè)務(wù)操作。

此外,應(yīng)用層也是微服務(wù)之間交互的通道,它可以調(diào)用其它微服務(wù)的應(yīng)用服務(wù),完成微服務(wù)之間的服務(wù)組合和編排。

這里我要提醒你一下:在設(shè)計(jì)和開發(fā)時(shí),不要將本該放在領(lǐng)域?qū)拥臉I(yè)務(wù)邏輯放到應(yīng)用層中實(shí)現(xiàn)。因?yàn)辇嫶蟮膽?yīng)用層會(huì)使領(lǐng)域模型失焦,時(shí)間一長你的微服務(wù)就會(huì)演化為傳統(tǒng)的三層架構(gòu),業(yè)務(wù)邏輯會(huì)變得混亂。

另外,應(yīng)用服務(wù)是在應(yīng)用層的,它負(fù)責(zé)服務(wù)的組合、編排和轉(zhuǎn)發(fā),負(fù)責(zé)處理業(yè)務(wù)用例的執(zhí)行順序以及結(jié)果的拼裝,以粗粒度的服務(wù)通過API網(wǎng)關(guān)向前端發(fā)布。還有,應(yīng)用服務(wù)還可以進(jìn)行安全認(rèn)證、權(quán)限校驗(yàn)、事務(wù)控制、發(fā)送或訂閱領(lǐng)域事件等。

3.領(lǐng)域?qū)?/strong>

領(lǐng)域?qū)拥淖饔檬菍?shí)現(xiàn)企業(yè)核心業(yè)務(wù)邏輯 ,通過各種校驗(yàn)手段保證業(yè)務(wù)的正確性。領(lǐng)域?qū)又饕w現(xiàn)領(lǐng)域模型的業(yè)務(wù)能力,它用來表達(dá)業(yè)務(wù)概念、業(yè)務(wù)狀態(tài)和業(yè)務(wù)規(guī)則。

領(lǐng)域?qū)影?strong>聚合根、實(shí)體、值對象、領(lǐng)域服務(wù) 等領(lǐng)域模型中的領(lǐng)域?qū)ο蟆?/p>

這里我要特別解釋一下其中幾個(gè)領(lǐng)域?qū)ο蟮年P(guān)系,以便你在設(shè)計(jì)領(lǐng)域?qū)拥臅r(shí)候能更加清楚。

首先,領(lǐng)域模型的業(yè)務(wù)邏輯主要是由實(shí)體和領(lǐng)域服務(wù)來實(shí)現(xiàn)的,其中實(shí)體會(huì)采用充血模型來實(shí)現(xiàn)所有與之相關(guān)的業(yè)務(wù)功能。

其次,你要知道,實(shí)體和領(lǐng)域服務(wù)在實(shí)現(xiàn)業(yè)務(wù)邏輯上不是同級的,當(dāng)領(lǐng)域中的某些功能,單一實(shí)體(或者值對象)不能實(shí)現(xiàn)時(shí),領(lǐng)域服務(wù)就會(huì)出馬,它可以組合聚合內(nèi)的多個(gè)實(shí)體(或者值對象),實(shí)現(xiàn)復(fù)雜的業(yè)務(wù)邏輯。

4.基礎(chǔ)層

基礎(chǔ)層是貫穿所有層的,它的作用就是為其它各層提供通用的技術(shù)和基礎(chǔ)服務(wù),包括第三方工具、驅(qū)動(dòng)、消息中間件、網(wǎng)關(guān)、文件、緩存以及數(shù)據(jù)庫 等。比較常見的功能還是提供數(shù)據(jù)庫持久化。

基礎(chǔ)層包含基礎(chǔ)服務(wù),它采用依賴倒置設(shè)計(jì),封裝基礎(chǔ)資源服務(wù),實(shí)現(xiàn)應(yīng)用層、領(lǐng)域?qū)优c基礎(chǔ)層的解耦,降低外部資源變化對應(yīng)用的影響。

比如說,在傳統(tǒng)架構(gòu)設(shè)計(jì)中,由于上層應(yīng)用對數(shù)據(jù)庫的強(qiáng)耦合,很多公司在架構(gòu)演進(jìn)中最擔(dān)憂的可能就是換數(shù)據(jù)庫了,因?yàn)橐坏└鼡Q數(shù)據(jù)庫,就可能需要重寫大部分的代碼,這對應(yīng)用來說是致命的。那采用依賴倒置的設(shè)計(jì)以后,應(yīng)用層就可以通過解耦來保持獨(dú)立的核心業(yè)務(wù)邏輯。當(dāng)數(shù)據(jù)庫變更時(shí),我們只需要更換數(shù)據(jù)庫基礎(chǔ)服務(wù)就可以了,這樣就將資源變更對應(yīng)用的影響降到了最低。

DDD分層架構(gòu)最重要的原則

在《實(shí)現(xiàn)領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)》一書中,DDD分層架構(gòu)有一個(gè)重要的原則:每層只能與位于其下方的層發(fā)生耦合 。

而架構(gòu)根據(jù)耦合的緊密程度又可以分為兩種:嚴(yán)格分層架構(gòu)和松散分層架構(gòu)。優(yōu)化后的DDD分層架構(gòu)模型就屬于嚴(yán)格分層架構(gòu),任何層只能對位于其直接下方的層產(chǎn)生依賴。而傳統(tǒng)的DDD分層架構(gòu)則屬于松散分層架構(gòu),它允許某層與其任意下方的層發(fā)生依賴。

建議采用嚴(yán)格分層架構(gòu)。

在嚴(yán)格分層架構(gòu)中,領(lǐng)域服務(wù)只能被應(yīng)用服務(wù)調(diào)用,而應(yīng)用服務(wù)只能被用戶接口層調(diào)用,服務(wù)是逐層對外封裝或組合的,依賴關(guān)系清晰。而在松散分層架構(gòu)中,領(lǐng)域服務(wù)可以同時(shí)被應(yīng)用層或用戶接口層調(diào)用,服務(wù)的依賴關(guān)系比較復(fù)雜且難管理,甚至容易使核心業(yè)務(wù)邏輯外泄。

試想下,如果領(lǐng)域?qū)又械哪硞€(gè)服務(wù)發(fā)生了重大變更,那該如何通知所有調(diào)用方同步調(diào)整和升級呢?但在嚴(yán)格分層架構(gòu)中,你只需要逐層通知上層服務(wù)就可以了。

代碼架構(gòu)

整潔架構(gòu)

整潔架構(gòu)又名“洋蔥架構(gòu) ”。為什么叫它洋蔥架構(gòu)?看看下面這張圖你就明白了。整潔架構(gòu)的層就像洋蔥片一樣,它體現(xiàn)了分層的設(shè)計(jì)思想。

在整潔架構(gòu)里,同心圓代表應(yīng)用軟件的不同部分,從里到外依次是領(lǐng)域模型、領(lǐng)域服務(wù)、應(yīng)用服務(wù)和最外圍的容易變化的內(nèi)容,比如用戶界面和基礎(chǔ)設(shè)施。

整潔架構(gòu)最主要的原則是依賴原則,它定義了各層的依賴關(guān)系,越往里依賴越低,代碼級別越高,越是核心能力。外圓代碼依賴只能指向內(nèi)圓,內(nèi)圓不需要知道外圓的任何情況。

fe48985e-0b1c-11ee-962d-dac502259ad0.png

在洋蔥架構(gòu)中,各層的職能是這樣劃分的:

領(lǐng)域模型實(shí)現(xiàn)領(lǐng)域內(nèi)核心業(yè)務(wù)邏輯,它封裝了企業(yè)級的業(yè)務(wù)規(guī)則。領(lǐng)域模型的主體是實(shí)體,一個(gè)實(shí)體可以是一個(gè)帶方法的對象,也可以是一個(gè)數(shù)據(jù)結(jié)構(gòu)和方法集合。

領(lǐng)域服務(wù)實(shí)現(xiàn)涉及多個(gè)實(shí)體的復(fù)雜業(yè)務(wù)邏輯。

應(yīng)用服務(wù)實(shí)現(xiàn)與用戶操作相關(guān)的服務(wù)組合與編排,它包含了應(yīng)用特有的業(yè)務(wù)流程規(guī)則,封裝和實(shí)現(xiàn)了系統(tǒng)所有用例。

最外層主要提供適配的能力,適配能力分為主動(dòng)適配和被動(dòng)適配。主動(dòng)適配主要實(shí)現(xiàn)外部用戶、網(wǎng)頁、批處理和自動(dòng)化測試等對內(nèi)層業(yè)務(wù)邏輯訪問適配。被動(dòng)適配主要是實(shí)現(xiàn)核心業(yè)務(wù)邏輯對基礎(chǔ)資源訪問的適配,比如數(shù)據(jù)庫、緩存、文件系統(tǒng)和消息中間件等。

紅圈內(nèi)的領(lǐng)域模型、領(lǐng)域服務(wù)和應(yīng)用服務(wù)一起組成軟件核心業(yè)務(wù)能力。

六邊形架構(gòu)

六邊形架構(gòu)又名“端口適配器架構(gòu)” 。追溯微服務(wù)架構(gòu)的淵源,一般都會(huì)涉及到六邊形架構(gòu)。

六邊形架構(gòu)的核心理念是:應(yīng)用是通過端口與外部進(jìn)行交互的 。我想這也是微服務(wù)架構(gòu)下API網(wǎng)關(guān)盛行的主要原因吧。

也就是說,在下圖的六邊形架構(gòu)中,紅圈內(nèi)的核心業(yè)務(wù)邏輯(應(yīng)用程序和領(lǐng)域模型)與外部資源(包括APP、Web應(yīng)用以及數(shù)據(jù)庫資源等)完全隔離,僅通過適配器進(jìn)行交互。它解決了業(yè)務(wù)邏輯與用戶界面的代碼交錯(cuò)問題,很好地實(shí)現(xiàn)了前后端分離。六邊形架構(gòu)各層的依賴關(guān)系與整潔架構(gòu)一樣,都是由外向內(nèi)依賴。

fe66739c-0b1c-11ee-962d-dac502259ad0.png

六邊形架構(gòu)將系統(tǒng)分為內(nèi)六邊形和外六邊形兩層,這兩層的職能劃分如下:

紅圈內(nèi)的六邊形實(shí)現(xiàn)應(yīng)用的核心業(yè)務(wù)邏輯;

外六邊形完成外部應(yīng)用、驅(qū)動(dòng)和基礎(chǔ)資源等的交互和訪問,對前端應(yīng)用以API主動(dòng)適配的方式提供服務(wù),對基礎(chǔ)資源以依賴倒置被動(dòng)適配的方式實(shí)現(xiàn)資源訪問。

六邊形架構(gòu)的一個(gè)端口可能對應(yīng)多個(gè)外部系統(tǒng),不同的外部系統(tǒng)也可能會(huì)使用不同的適配器,由適配器負(fù)責(zé)協(xié)議轉(zhuǎn)換。這就使得應(yīng)用程序能夠以一致的方式被用戶、程序、自動(dòng)化測試和批處理腳本使用。

三種模型對比

雖然DDD分層架構(gòu)、整潔架構(gòu)、六邊形架構(gòu)的架構(gòu)模型表現(xiàn)形式不一樣,但你不要被它們的表象所迷惑,這三種架構(gòu)模型的設(shè)計(jì)思想正是微服務(wù)架構(gòu)高內(nèi)聚低耦合原則的完美體現(xiàn),而它們身上閃耀的正是以領(lǐng)域模型為中心的設(shè)計(jì)思想。

fe8bd2fe-0b1c-11ee-962d-dac502259ad0.png

我們看下上面這張圖,結(jié)合圖示對這三種架構(gòu)模型做一個(gè)分析。

請你重點(diǎn)關(guān)注圖中的紅色線框,它們是非常重要的分界線,這三種架構(gòu)里面都有,它的作用就是將核心業(yè)務(wù)邏輯與外部應(yīng)用、基礎(chǔ)資源進(jìn)行隔離。

紅色框內(nèi)部主要實(shí)現(xiàn)核心業(yè)務(wù)邏輯,但核心業(yè)務(wù)邏輯也是有差異的,有的業(yè)務(wù)邏輯屬于領(lǐng)域模型的能力,有的則屬于面向用戶的用例和流程編排能力。按照這種功能的差異,我們在這三種架構(gòu)中劃分了應(yīng)用層和領(lǐng)域?qū)?,來承?dān)不同的業(yè)務(wù)邏輯。

領(lǐng)域?qū)訉?shí)現(xiàn)面向領(lǐng)域模型,實(shí)現(xiàn)領(lǐng)域模型的核心業(yè)務(wù)邏輯,屬于原子模型,它需要保持領(lǐng)域模型和業(yè)務(wù)邏輯的穩(wěn)定,對外提供穩(wěn)定的細(xì)粒度的領(lǐng)域服務(wù),所以它處于架構(gòu)的核心位置。

應(yīng)用層實(shí)現(xiàn)面向用戶操作相關(guān)的用例和流程,對外提供粗粒度的API服務(wù)。它就像一個(gè)齒輪一樣進(jìn)行前臺(tái)應(yīng)用和領(lǐng)域?qū)拥倪m配,接收前臺(tái)需求,隨時(shí)做出響應(yīng)和調(diào)整,盡量避免將前臺(tái)需求傳導(dǎo)到領(lǐng)域?qū)?。?yīng)用層作為配速齒輪則位于前臺(tái)應(yīng)用和領(lǐng)域?qū)又g。

COLA4.0

GitHub:https://github.com/alibaba/COLA 作者博客:https://blog.csdn.net/significantfrank/article/details/110934799

生成器:https://start.aliyun.com/bootstrap.html

fea732c4-0b1c-11ee-962d-dac502259ad0.png

基于 Spring Boot + MyBatis Plus + Vue & Element 實(shí)現(xiàn)的后臺(tái)管理系統(tǒng) + 用戶小程序,支持 RBAC 動(dòng)態(tài)權(quán)限、多租戶、數(shù)據(jù)權(quán)限、工作流、三方登錄、支付、短信、商城等功能

項(xiàng)目地址:https://github.com/YunaiV/ruoyi-vue-pro

視頻教程:https://doc.iocoder.cn/video/

DDD實(shí)戰(zhàn)

feb9e162-0b1c-11ee-962d-dac502259ad0.png

模塊劃分如下

fed0aaa0-0b1c-11ee-962d-dac502259ad0.png

本次我們重點(diǎn)完成

賬戶模塊

廣告管理模塊

廣告下發(fā)模塊

戰(zhàn)略設(shè)計(jì)

戰(zhàn)略設(shè)計(jì)階段:此階段主要是依賴于事件風(fēng)暴(可理解為基于事件流程的頭腦風(fēng)暴),來呈現(xiàn)出產(chǎn)品的發(fā)展方向以及核心流程和場景,并文檔化。

1.產(chǎn)品要解決的問題,以及從用戶角度歸納出典型業(yè)務(wù)場景,落實(shí)文檔 -----> 產(chǎn)品愿景、場景分析

2.找出上一步總結(jié)出的關(guān)鍵名詞,作為各個(gè)場景的實(shí)體 -----> 領(lǐng)域建模:找出領(lǐng)域?qū)ο?/p>

3.根據(jù)上一步總結(jié)出實(shí)體,總結(jié)出之間的關(guān)系(聚合根、值對象、普通實(shí)體),劃分出聚合 -----> 領(lǐng)域建模:定義聚合

4.以上一步歸納出的聚合為單位,根據(jù)業(yè)務(wù)場景將聚合分組,得到限界上下文(也就是所屬的領(lǐng)域) ----->領(lǐng)域建模:定義界限上下文

在第 1 步落實(shí)文檔后,后面的 2,3,4 領(lǐng)域建模階段都要不斷的參照第 1 步總結(jié)出的業(yè)務(wù)流程場景來進(jìn)行拆解與合并;產(chǎn)品愿景、場景分析 兩個(gè)階段是從宏觀到微觀的過程,而 領(lǐng)域建模階段是從微觀到宏觀的過程,也就是自底向上的思想。整體就像是總分、分總的過程。

產(chǎn)品愿景

產(chǎn)品愿景是對產(chǎn)品頂層價(jià)值設(shè)計(jì),對產(chǎn)品目標(biāo)用戶、核心價(jià)值、差異化競爭點(diǎn)等信息達(dá)成一致,避免產(chǎn)品偏離方向。

服務(wù)用戶

想投廣告的人。廣告主以及代理商的優(yōu)化師

提供能力

給廣告主提供豐富的廣告資源,實(shí)現(xiàn)廣告的管理和投放。廣告投放效果,以及效率

提高交互效率。提供open-api,可以讓客戶通過程序化的方式管理廣告。

定位

業(yè)界先進(jìn)的一站式效果投放平臺(tái)。提供RTA,DPA,OCPX等專業(yè)能力

優(yōu)勢

依托于手機(jī)廠商提供的數(shù)據(jù)能力,給廣告主提供更好的投放效果。

場景分析

fee0a892-0b1c-11ee-962d-dac502259ad0.png

領(lǐng)域建模

領(lǐng)域建模是通過對業(yè)務(wù)和問題域進(jìn)行分析,建立領(lǐng)域模型。

向上通過限界上下文指導(dǎo)微服務(wù)邊界設(shè)計(jì),向下通過聚合指導(dǎo)實(shí)體對象設(shè)計(jì)。

領(lǐng)域建模是一個(gè)收斂的過程,分三步:

第一步找出領(lǐng)域?qū)嶓w和值對象等領(lǐng)域?qū)ο螅?/p>

第二步找出聚合根,根據(jù)實(shí)體、值對象與聚合根的依賴關(guān)系,建立聚合;

第三步根據(jù)業(yè)務(wù)及語義邊界等因素,定義限界上下文。

第一步:找出實(shí)體和值對象等領(lǐng)域?qū)ο?/p>

根據(jù)場景分析,分析并找出發(fā)起或產(chǎn)生這些命令或領(lǐng)域事件的實(shí)體和值對象,將與實(shí)體或值對象有關(guān)的命令和事件聚集到實(shí)體。

fef84f88-0b1c-11ee-962d-dac502259ad0.png

第二步:定義聚合

定義聚合前,先找出聚合根。然后找出與聚合根緊密依賴的實(shí)體和值對象。我們發(fā)現(xiàn)審批意見、審批規(guī)則和請假單緊密關(guān)聯(lián),組織關(guān)系和人員緊密關(guān)聯(lián)。

ff2fd71e-0b1c-11ee-962d-dac502259ad0.png

第三步:定義限界上下文

把整個(gè)定義為廣告管理限界上下文

通用語言

廣告業(yè)務(wù):

中文 英文
廣告計(jì)劃 AdCampaign
廣告組 AdGroup
廣告定向 AdTargeting
廣告創(chuàng)意 AdCreative
廣告素材 AdAsset
操作日志 OperationLog
賬號(hào) Account

微服務(wù)拆分

理論上一個(gè)限界上下文就可以設(shè)計(jì)為一個(gè)微服務(wù),但還需要綜合考慮多種外部因素,比如:職責(zé)單一性、敏態(tài)與穩(wěn)態(tài)業(yè)務(wù)分離、非功能性需求(如彈性伸縮、版本發(fā)布頻率和安全等要求)、軟件包大小、團(tuán)隊(duì)溝通效率和技術(shù)異構(gòu)等非業(yè)務(wù)要素。

可以微服務(wù)的拆分粒度大一些,但是聚合和限界上下文一定要邊界清晰,后續(xù)隨著某些功能逐漸變大再去拆分也會(huì)比較容易

戰(zhàn)術(shù)設(shè)計(jì)

有了戰(zhàn)略設(shè)計(jì)階段的結(jié)果,反而戰(zhàn)術(shù)設(shè)計(jì)階段相對清晰一些。

1.按照 DDD 四層模型建包,咱們這里使用COLA生成的包結(jié)構(gòu)

2.確定聚合中的對象關(guān)系,定義哪些是實(shí)體,哪些是值對象,具體字段都有什么。

3.通過戰(zhàn)略設(shè)計(jì)階段文檔中的命令、事件來編排充血模型的領(lǐng)域?qū)ο?,?gòu)建應(yīng)用服務(wù)與領(lǐng)域服務(wù)

詳細(xì)設(shè)計(jì)

技術(shù)選型跟上面的關(guān)系不大??梢允褂肅OLA4.0分層框架。

使用SpringCloud技術(shù)棧,以及根據(jù)業(yè)務(wù)建模選擇中間件。





審核編輯:劉清

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報(bào)投訴

原文標(biāo)題:2萬字帶你入門DDD

文章出處:【微信號(hào):芋道源碼,微信公眾號(hào):芋道源碼】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏

    評論

    相關(guān)推薦

    萬字長文,看懂激光基礎(chǔ)知識(shí)!

    深入介紹激光基礎(chǔ)知識(shí),幫助您輕松理解激光領(lǐng)域的關(guān)鍵概念和原理。
    的頭像 發(fā)表于 12-20 09:49 ?60次閱讀
    <b class='flag-5'>萬字</b>長文,看懂激光基礎(chǔ)知識(shí)!

    在DVEVM上通過ddd運(yùn)行Demo

    電子發(fā)燒友網(wǎng)站提供《在DVEVM上通過ddd運(yùn)行Demo.pdf》資料免費(fèi)下載
    發(fā)表于 10-15 10:05 ?0次下載
    在DVEVM上通過<b class='flag-5'>ddd</b>運(yùn)行Demo

    TVP5146M2 VBI快速入門

    電子發(fā)燒友網(wǎng)站提供《TVP5146M2 VBI快速入門.pdf》資料免費(fèi)下載
    發(fā)表于 09-30 11:10 ?0次下載
    TVP5146M<b class='flag-5'>2</b> VBI快速<b class='flag-5'>入門</b>

    DDD學(xué)習(xí)與感悟——向屎山?jīng)_鋒

    軟件系統(tǒng)是通過軟件開發(fā)來解決某一個(gè)業(yè)務(wù)領(lǐng)域或問題單元而產(chǎn)生的一個(gè)交付物。而通過軟件設(shè)計(jì)可以幫助我們開發(fā)出更加健壯的軟件系統(tǒng)。因此,軟件設(shè)計(jì)是從業(yè)務(wù)領(lǐng)域到軟件開發(fā)之間的橋梁。而DDD是軟件設(shè)計(jì)中的其中
    的頭像 發(fā)表于 09-24 13:31 ?214次閱讀
    <b class='flag-5'>DDD</b>學(xué)習(xí)與感悟——向屎山?jīng)_鋒

    第三篇-V1.5 TB6612電機(jī)pwm控制STM32智能小車 STM32F103C8T6單片機(jī)

    通過合理的硬件設(shè)計(jì)和詳細(xì)的視頻筆記介紹,硬件使用STM32F103主控資料多方便學(xué)習(xí),通過3萬字筆記、12多個(gè)小時(shí)視頻、20多章節(jié)代碼手把手教會(huì)你如何開發(fā)和調(diào)試。
    的頭像 發(fā)表于 08-12 18:29 ?1583次閱讀
    第三篇-V1.5 TB6612電機(jī)pwm控制STM32智能小車 STM32F103C8T6單片機(jī)

    第一篇:V1.5-STM32f103c8t6智能小車筆記 標(biāo)準(zhǔn)庫開發(fā) 6612電機(jī)驅(qū)動(dòng)新手入門項(xiàng)目

    這是全網(wǎng)最詳細(xì)、性價(jià)比最高的STM32實(shí)戰(zhàn)項(xiàng)目入門教程,通過合理的硬件設(shè)計(jì)和詳細(xì)的視頻筆記介紹,硬件使用STM32F103主控資料多方便學(xué)習(xí),通過3萬字筆記、12多個(gè)小時(shí)視頻、20多章節(jié)代碼手把手教會(huì)你如何開發(fā)和調(diào)試。讓你更快掌握嵌入式系統(tǒng)開發(fā)。
    的頭像 發(fā)表于 08-12 18:25 ?1612次閱讀
    第一篇:V1.5-STM32f103c8t6智能小車筆記 標(biāo)準(zhǔn)庫開發(fā) 6612電機(jī)驅(qū)動(dòng)新手<b class='flag-5'>入門</b>項(xiàng)目

    1.3萬字!詳解半導(dǎo)體先進(jìn)封裝行業(yè),現(xiàn)狀及發(fā)展趨勢!

    。 典型封裝技術(shù)包括:1)倒片封裝(Flip-Chip):芯片倒置,舍棄金屬引線,利用凸塊連接;2)扇入型/扇出型封裝(Fan-In/Fan-Out):在晶圓上進(jìn)行整體封裝,成本更低,關(guān)鍵工藝為重新布線(RDL);3)2.5D/3D封裝:2.5D封裝中芯片位于硅中介層上,3D封裝舍
    的頭像 發(fā)表于 07-03 08:44 ?1943次閱讀
    1.3<b class='flag-5'>萬字</b>!詳解半導(dǎo)體先進(jìn)封裝行業(yè),現(xiàn)狀及發(fā)展趨勢!

    萬字長文淺談系統(tǒng)穩(wěn)定性建設(shè)

    流程:需求階段,研發(fā)階段,測試階段,上線階段,運(yùn)維階段;整個(gè)流程中的所有參與人員:產(chǎn)品,研發(fā),測試,運(yùn)維人員都應(yīng)關(guān)注系統(tǒng)的穩(wěn)定性。業(yè)務(wù)的發(fā)展及系統(tǒng)建設(shè)過程中,穩(wěn)定性就是那個(gè)1,其他的是1后面的0,沒有穩(wěn)定性,就好比將
    的頭像 發(fā)表于 07-02 10:31 ?363次閱讀
    <b class='flag-5'>萬字</b>長文淺談系統(tǒng)穩(wěn)定性建設(shè)

    這個(gè)調(diào)試工具咋賣39.9?分析了原理后,我悟了

    ——功能(第2章),電路設(shè)計(jì)實(shí)現(xiàn)原理(3-5章),組裝步驟(第6章),軟件說明(第7章)展開手把手教學(xué)。助你快速入門并學(xué)透嵌入式產(chǎn)品的基礎(chǔ)設(shè)計(jì)理念。前方萬字干貨預(yù)警!建
    的頭像 發(fā)表于 06-21 08:04 ?180次閱讀
    這個(gè)調(diào)試工具咋賣39.9?分析了原理后,我悟了

    MiniMax推出“海螺AI”,支持超長文本處理

    近日,大模型公司MiniMax宣布,其全新產(chǎn)品“海螺AI”已正式上架。這款強(qiáng)大的AI工具支持高達(dá)200ktokens的上下文長度,能夠在1秒內(nèi)處理近3萬字的文本。
    的頭像 發(fā)表于 05-17 09:30 ?731次閱讀

    AI初創(chuàng)企業(yè)推MoE混合專家模型架構(gòu)新品abab 6.5

    losoev 6.5s:與 losoev 6.5 共享相同的訓(xùn)練技術(shù)和數(shù)據(jù),但效率更高,同樣支持 200k tokens 的上下文長度,且能夠在 1 秒鐘內(nèi)處理近 3 萬字的文本。
    的頭像 發(fā)表于 04-17 15:06 ?502次閱讀

    阿里通義千問重磅升級,免費(fèi)開放1000萬字長文檔處理功能

    近日,阿里巴巴旗下的人工智能應(yīng)用通義千問迎來重磅升級,宣布向所有人免費(fèi)開放1000萬字的長文檔處理功能,這一創(chuàng)新舉措使得通義千問成為全球文檔處理容量第一的AI應(yīng)用。
    的頭像 發(fā)表于 03-26 11:09 ?785次閱讀

    馬斯克狀告OpenAI,OpenAI回應(yīng)馬斯克訴訟

    馬斯克在長達(dá)46頁、1.4萬字的訴訟文件中,控訴OpenAI背離了其初衷——即致力于開發(fā)開源人工通用智能(AGI)并服務(wù)全人類。
    的頭像 發(fā)表于 03-04 15:33 ?908次閱讀

    Purple Pi 帶你7天入門OpenHarmony

    大家好!我是一名剛入門OpenHarmony的小白,很高興能夠和大家一起學(xué)習(xí)OpenHarmony,在本系列文章里,我將分享使用PurplePiOH從零開始學(xué)習(xí)開源鴻蒙的整個(gè)流程,并提供每一步需要
    的頭像 發(fā)表于 02-19 13:20 ?482次閱讀
    Purple Pi <b class='flag-5'>帶你</b>7天<b class='flag-5'>入門</b>OpenHarmony

    如何用AI聊天機(jī)器人寫出萬字長文

    如何用AI聊天機(jī)器人寫出萬字長文
    的頭像 發(fā)表于 12-26 16:25 ?1056次閱讀
    主站蜘蛛池模板: 麻豆国产精品AV色拍综合| 国产成人一区二区三区在线观看| 人人爽久久久噜噜噜丁香AV| 超碰caoporn| 射漂亮黑b丝女| 国产午夜一区二区三区免费视频| 亚洲色大成网站www久久九九| 久久性生大片免费观看性| 99国产精品久久人妻| 日韩成人在线视频| 国产免费69成人精品视频| 一个人免费完整在线观看影院| 美女脱三角裤| 朝鲜黄色录像| 亚洲国产中文在线视频免费| 伦理片天堂eeuss影院| 扒开 浓密 毛| 亚洲国产成人久久精品影视| 久久久这里有精品999| thermo脱色摇床安卓下载| 偷拍自怕亚洲在线第7页| 九九热免费在线观看| 99久久久A片无码国产精| 天堂so导航| 久久国产高清字幕中文| hd性欧美俱乐部中文| 洗濯屋H纯肉动漫在线观看| 久久精品国产免费中文| porono日本xxx| 雪恋电影完整版免费观看| 巨胸美乳中文在线观看| 攻把受做得合不拢腿play| 一色狗影院| 日日啪在线影院百度| 花蝴蝶在线观看中字| www.欧美与曽交性video| 亚洲AV无码偷拍在线观看| 免费看毛片的网址| 国产九色在线| 97人摸人人澡人人人超一碰| 手机毛片免费看|