對(duì)領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)的理解與實(shí)踐(對(duì)領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)的理解與實(shí)踐怎么寫)
領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)(domain-Driven-Design)是一種針對(duì)大型復(fù)雜系統(tǒng)的領(lǐng)域建模與分析方法論。
2003 年,Eric Evans 發(fā)布《Domain-Driven Design: Tackling Complexity in the Heart of Software》(領(lǐng)域驅(qū)動(dòng)設(shè)計(jì):軟件核心復(fù)雜性應(yīng)對(duì)之道),其中定義了DDD。
DDD改變了傳統(tǒng)軟件開(kāi)發(fā)針對(duì)數(shù)據(jù)庫(kù)進(jìn)行的建模方法;DDD先對(duì)業(yè)務(wù)領(lǐng)域進(jìn)行分析,建立領(lǐng)域模型,根據(jù)領(lǐng)域模型驅(qū)動(dòng)代碼設(shè)計(jì)。合理運(yùn)用面向?qū)ο蟮母邇?nèi)聚低耦合設(shè)計(jì)要素,降低整個(gè)系統(tǒng)的業(yè)務(wù)復(fù)雜性,并使得系統(tǒng)具有更好的擴(kuò)展性,更好的應(yīng)對(duì)多變的業(yè)務(wù)需求。
領(lǐng)域 Domain
一個(gè)領(lǐng)域就是一個(gè)問(wèn)題域,只要是同一個(gè)領(lǐng)域,那問(wèn)題域就相同。
只要確定了系統(tǒng)所屬的領(lǐng)域,那么這個(gè)系統(tǒng)的核心業(yè)務(wù),即要解決的問(wèn)題以及問(wèn)題的邊界就基本確定了。
舉例
陌生人社交領(lǐng)域,包含有聊天,用戶推薦,朋友圈等核心環(huán)節(jié)。 只要是這個(gè)領(lǐng)域,一般都會(huì)有這些相同的核心業(yè)務(wù),因?yàn)樗麄円鉀Q問(wèn)題的本質(zhì)是一樣的,就是交友。
每一個(gè)領(lǐng)域,都有一個(gè)對(duì)應(yīng)的領(lǐng)域模型,領(lǐng)域模型能夠很好的幫我們解決復(fù)雜的業(yè)務(wù)問(wèn)題。
驅(qū)動(dòng)設(shè)計(jì)
在DDD中,以領(lǐng)域(domain)為邊界,分析領(lǐng)域的核心問(wèn)題,再設(shè)計(jì)對(duì)應(yīng)的領(lǐng)域模型,最后通過(guò)領(lǐng)域模型驅(qū)動(dòng)代碼設(shè)計(jì)的實(shí)現(xiàn)。這樣設(shè)計(jì)的系統(tǒng)才有合理的分層與解耦,對(duì)以后業(yè)務(wù)的迭代開(kāi)發(fā),代碼的維護(hù)才更加容易。
然而很多互聯(lián)網(wǎng)公司,為了追求快速的上線,都是模型都沒(méi)有想清楚就開(kāi)始寫代碼,這就導(dǎo)致了后續(xù)代碼維護(hù)困難,無(wú)法擴(kuò)展。修改bug同時(shí)又引入新的bug,反反復(fù)復(fù),進(jìn)入惡性循環(huán)。
當(dāng)然,這跟梳理清楚領(lǐng)域模型需要一定時(shí)間,這與初創(chuàng)型的互聯(lián)網(wǎng)公司需求快速上線有點(diǎn)相悖,但是,這點(diǎn)時(shí)間的投入是非常值得的。因?yàn)榭梢员苊饬讼到y(tǒng)上線后不久又得重構(gòu)的問(wèn)題。
概念總結(jié)
- 領(lǐng)域就是問(wèn)題域
- 模型驅(qū)動(dòng)的思想:通過(guò)建立領(lǐng)域模型來(lái)解決領(lǐng)域中的核心問(wèn)題
- 領(lǐng)域建模的目標(biāo):針對(duì)我們?cè)陬I(lǐng)域中核心問(wèn)題,而不是整個(gè)領(lǐng)域中的所有問(wèn)題
- 領(lǐng)域模型設(shè)計(jì):設(shè)計(jì)時(shí)應(yīng)考慮一定的抽象性、通用性,以及復(fù)用價(jià)值
- 代碼實(shí)現(xiàn):通過(guò)領(lǐng)域模型驅(qū)動(dòng)代碼的實(shí)現(xiàn),確保代碼讓領(lǐng)域模型落地,代碼最終能解決問(wèn)題
為什么需要DDD
系統(tǒng)復(fù)雜性
耦合
隨著產(chǎn)品不斷的迭代,業(yè)務(wù)邏輯變得越來(lái)越復(fù)雜,系統(tǒng)也越來(lái)越龐大。模塊彼此互相關(guān)聯(lián)、耦合。導(dǎo)致增加或修改一個(gè)功能變得異常艱難,同時(shí)功能間的界限也變得模糊,職責(zé)不再清晰。這個(gè)時(shí)候就需要進(jìn)行重構(gòu),拆分。
雖然架構(gòu)本身是隨著業(yè)務(wù)進(jìn)行不斷演進(jìn)的;但是,如果架構(gòu)初始設(shè)計(jì)不體現(xiàn)出業(yè)務(wù)的模型,那么新需求就無(wú)法體現(xiàn)在現(xiàn)有架構(gòu)上,導(dǎo)致不斷腐化,不斷重構(gòu)。
內(nèi)聚
貧血模型 Anemic Domain Object
domain object僅用作數(shù)據(jù)載體,而沒(méi)有行為和動(dòng)作的領(lǐng)域?qū)ο蟆?/p>
指領(lǐng)域?qū)ο罄镏挥術(shù)et和set方法,沒(méi)有相關(guān)領(lǐng)域?qū)ο蟮臉I(yè)務(wù)邏輯。業(yè)務(wù)邏輯放在業(yè)務(wù)層。
充血模型 Rich Domain Object
將業(yè)務(wù)邏輯和對(duì)象存儲(chǔ)放在domain object里面,業(yè)務(wù)層只是簡(jiǎn)單進(jìn)行小部分業(yè)務(wù)的封裝及其他domain的編排。
面向?qū)ο笤O(shè)計(jì),符合單一職責(zé)設(shè)計(jì)。
貧血 vs 充血
貧血模型的domain object很輕量,這導(dǎo)致業(yè)務(wù)層的復(fù)雜,domain object相關(guān)的業(yè)務(wù)邏輯散布在各個(gè)業(yè)務(wù)層,造成業(yè)務(wù)邏輯的冗余以及原本domain object的定義就變得相對(duì)模糊,這就是貧血癥引起的失憶癥。
而采用領(lǐng)域開(kāi)發(fā)的方式,將數(shù)據(jù)和行為封裝在一起,與業(yè)務(wù)對(duì)象相映射;領(lǐng)域?qū)ο舐氊?zé)清晰,將相關(guān)業(yè)務(wù)聚合到領(lǐng)域?qū)ο髢?nèi)部。
微服務(wù)
DDD 的本質(zhì)是一種軟件設(shè)計(jì)方法論,而微服務(wù)架構(gòu)是具體的實(shí)現(xiàn)方式。微服務(wù)架構(gòu)并沒(méi)有定義對(duì)復(fù)雜系統(tǒng)進(jìn)行分解的具體方法論,而 DDD 正好就是解決方案。
微服務(wù)架構(gòu)強(qiáng)調(diào)從業(yè)務(wù)維度來(lái)分治系統(tǒng)的復(fù)雜度,而DDD也是同樣的著重業(yè)務(wù)視角。
DDD能帶來(lái)什么
- 建立通用語(yǔ)言: 圍繞領(lǐng)域模型建立的一種語(yǔ)言,團(tuán)隊(duì)所有成員都使用這種語(yǔ)言進(jìn)行溝通和活動(dòng)
- 驅(qū)動(dòng)代碼設(shè)計(jì):領(lǐng)域建立模型,模型指導(dǎo)設(shè)計(jì),設(shè)計(jì)產(chǎn)出代碼
- 解決核心問(wèn)題:模型的設(shè)計(jì)中心就是核心域,就是解決核心的問(wèn)題
DDD建模
戰(zhàn)略設(shè)計(jì)
戰(zhàn)略設(shè)計(jì)就是從宏觀角度對(duì)領(lǐng)域進(jìn)行建模。劃分出業(yè)務(wù)的邊界,組織架構(gòu),系統(tǒng)架構(gòu)。
DDD中,對(duì)系統(tǒng)的劃分是基于領(lǐng)域的,也是基于業(yè)務(wù)的。
通用語(yǔ)言Ubiquitous Language
通用語(yǔ)言是指確定統(tǒng)一的領(lǐng)域術(shù)語(yǔ),提高開(kāi)發(fā)人員與領(lǐng)域?qū)<抑g的溝通效率。
一旦確定了統(tǒng)一語(yǔ)言,無(wú)論是與領(lǐng)域?qū)<业挠懻?,還是最終的實(shí)現(xiàn)代碼,都可以通過(guò)使用相同的術(shù)語(yǔ),清晰準(zhǔn)確地定義領(lǐng)域知識(shí)。
當(dāng)確認(rèn)整個(gè)團(tuán)隊(duì)統(tǒng)一的語(yǔ)言后,就可以開(kāi)始進(jìn)行領(lǐng)域建模。
領(lǐng)域和子域
領(lǐng)域Domain
一個(gè)領(lǐng)域本質(zhì)上可以理解為就是一個(gè)問(wèn)題域。只要我們確定了系統(tǒng)所屬的領(lǐng)域,那這個(gè)系統(tǒng)的核心業(yè)務(wù),即要解決的關(guān)鍵問(wèn)題、問(wèn)題的范圍邊界就基本確定了。
舉例
- 社交領(lǐng)域:關(guān)鍵問(wèn)題是用戶推薦,聊天
- 電商領(lǐng)域:關(guān)鍵問(wèn)題是購(gòu)物,訂單,物流
子域Subdomain
如果一個(gè)領(lǐng)域過(guò)于復(fù)雜,涉及到的領(lǐng)域概念、業(yè)務(wù)規(guī)則、交互流程太多,導(dǎo)致沒(méi)辦法直接針對(duì)這個(gè)大的領(lǐng)域進(jìn)行領(lǐng)域建模。這時(shí)就需要將領(lǐng)域進(jìn)行拆分,本質(zhì)上就是把大問(wèn)題拆分為小問(wèn)題,把一個(gè)大的領(lǐng)域劃分為了多個(gè)小的領(lǐng)域(子域),那最關(guān)鍵的就是要理清每個(gè)子域的邊界
子域可以根據(jù)自身重要性和功能屬性劃分為三類子域:
- 核心域:公司核心產(chǎn)品和業(yè)務(wù)的領(lǐng)域
- 支撐子域:不包含決定產(chǎn)品和公司核心競(jìng)爭(zhēng)力的功能,也不包含通用功能的子域
- 通用子域:被多個(gè)子域使用的通用功能子域
每個(gè)領(lǐng)域的劃分都不一樣。對(duì)相同領(lǐng)域公司而言,其核心,支撐,通用的子域也可能有不一樣的地方,但大體上基本都是一樣的。
舉例
社交領(lǐng)域的劃分
- 核心域:用戶推薦,聊天
- 支撐子域:客服,反垃圾
- 通用子域:消息推送
限界上下文Bounded Context
限界上下文
限界指劃分邊界,上下文對(duì)應(yīng)一個(gè)聚合,限界上下文可以理解為業(yè)務(wù)的邊界。
一個(gè)子域?qū)?yīng)一個(gè)或多個(gè)限界上下文。如果對(duì)應(yīng)多個(gè)上下文,則可以考慮子域是否要再進(jìn)行細(xì)粒度的拆分。
限界上下文的目的是為了更加明確領(lǐng)域模型的職責(zé)和范圍
劃分限界上下文
三個(gè)原則:
- 概念相同,含義不同(通用語(yǔ)言):如果一個(gè)模型在一個(gè)上下文里面有歧義,那有歧義的地方就是邊界所在,應(yīng)該把它們拆到不同的限界上下文中。
- 外部系統(tǒng):有時(shí)候系統(tǒng)需要同外部系統(tǒng)交互,這時(shí)可以把與外部系統(tǒng)交互的那部分拆分出去以實(shí)現(xiàn)更好的擴(kuò)展性。這樣一旦外部系統(tǒng)發(fā)生了變化,就不會(huì)影響到我們的核心業(yè)務(wù)邏輯。
- 組織擴(kuò)展:盡量不要兩個(gè)團(tuán)隊(duì)一起在一個(gè)限界上下文里面開(kāi)發(fā),因?yàn)檫@樣可能會(huì)存在溝通不順暢、集成困難等問(wèn)題。
組織架構(gòu)
康威定律
任何組織在設(shè)計(jì)一套系統(tǒng)時(shí),所交付的設(shè)計(jì)方案在結(jié)構(gòu)上都與該組織的溝通結(jié)構(gòu)保持一致。
團(tuán)隊(duì)結(jié)構(gòu)就是組織結(jié)構(gòu),限界上下文就是系統(tǒng)的業(yè)務(wù)結(jié)構(gòu)。所以,團(tuán)隊(duì)結(jié)構(gòu)應(yīng)該盡量和限界上下文保持一致。
舉例
社交領(lǐng)域中,訂單子域?qū)?yīng)訂單上下文
上下文映射
從宏觀上看每個(gè)上下文之間的關(guān)系,可以更好理解各個(gè)上下文之間的依賴關(guān)系。
梳理清楚上下文之間的關(guān)系是為了:
- 任務(wù)更好拆分,一個(gè)開(kāi)發(fā)人員可以全身心的投入到相關(guān)的一個(gè)單獨(dú)的上下文中
- 溝通更加順暢,一個(gè)上下文可以明確自己對(duì)其他上下文的依賴關(guān)系,從而使得團(tuán)隊(duì)內(nèi)開(kāi)發(fā)直接更好的對(duì)接
- 每個(gè)團(tuán)隊(duì)在它的上下文中能夠更加明確自己領(lǐng)域內(nèi)的概念,因?yàn)樯舷挛氖穷I(lǐng)域的解系統(tǒng)
舉例
聊天上下文依賴消息推送,推廣上下文也依賴消息推送
戰(zhàn)術(shù)建模
戰(zhàn)術(shù)建模是從微觀角度對(duì)上下文進(jìn)行建模。
梳理清楚聚合根,實(shí)體,值對(duì)象,領(lǐng)域服務(wù),領(lǐng)域事件,資源庫(kù)等。
實(shí)體Entity
當(dāng)一個(gè)對(duì)象可以由標(biāo)識(shí)進(jìn)行區(qū)分時(shí),這種對(duì)象稱為實(shí)體
和數(shù)據(jù)庫(kù)中的實(shí)體是不同的,這里的實(shí)體是從業(yè)務(wù)角度進(jìn)行劃分的。
實(shí)體:
- 具有唯一標(biāo)識(shí)
- 持久化
- 可變
舉例
社交中的用戶即為實(shí)體,可以通過(guò)用戶唯一的id進(jìn)行區(qū)分。
值對(duì)象value object
當(dāng)一個(gè)對(duì)象用于對(duì)事物進(jìn)行描述而沒(méi)有唯一標(biāo)識(shí)時(shí),它被稱作值對(duì)象。
在實(shí)踐中,需要保證值對(duì)象創(chuàng)建后就不能被修改,即不允許外部再修改其屬性。
例如:年齡,聊天表情符號(hào)( :stuck_out_tongue:: 吐舌 (U 1F61B))
習(xí)慣了使用數(shù)據(jù)庫(kù)的數(shù)據(jù)建模后,很容易將所有對(duì)象看作實(shí)體
聚合根Aggregate Root
聚合是一組相關(guān)對(duì)象的集合,作為一個(gè)整體被外界訪問(wèn),聚合根是這個(gè)聚合的根節(jié)點(diǎn)。
聚合由根實(shí)體,值對(duì)象和實(shí)體組成。(聚合根里面有多少個(gè)實(shí)體,由領(lǐng)域建模決定)
外部對(duì)象需要訪問(wèn)聚合內(nèi)的實(shí)體時(shí),只能通過(guò)聚合根進(jìn)行訪問(wèn),而不能直接訪問(wèn)
舉例
一個(gè)訂單是一個(gè)聚合根,訂單購(gòu)買的商品是實(shí)體,收貨地址是值對(duì)象。
領(lǐng)域服務(wù)Domain Service
領(lǐng)域服務(wù)
一些既不是實(shí)體,也不是值對(duì)象的范疇的領(lǐng)域行為或操作,可以放到領(lǐng)域服務(wù)中。用來(lái)處理業(yè)務(wù)邏輯,協(xié)調(diào)領(lǐng)域?qū)ο髞?lái)完成相關(guān)業(yè)務(wù)。
例如,有些業(yè)務(wù)邏輯不適合放到領(lǐng)域?qū)ο笾校驅(qū)嶓w之間的業(yè)務(wù)協(xié)調(diào),這些業(yè)務(wù)邏輯都可以放到領(lǐng)域服務(wù)中。
特征
- 與領(lǐng)域相關(guān)的操作如執(zhí)行一個(gè)業(yè)務(wù)操作過(guò)程,但它又并不適合放入實(shí)體與值對(duì)象中
- 操作是無(wú)狀態(tài)的
- 對(duì)領(lǐng)域?qū)ο筮M(jìn)行轉(zhuǎn)換,或以多個(gè)領(lǐng)域?qū)ο笞鳛檩斎脒M(jìn)行計(jì)算,結(jié)果產(chǎn)生一個(gè)值對(duì)象
當(dāng)采用微服務(wù)架構(gòu)風(fēng)格,一切領(lǐng)域邏輯的對(duì)外暴露均需要通過(guò)領(lǐng)域服務(wù)來(lái)進(jìn)行。
如原本由聚合根暴露的業(yè)務(wù)邏輯也需要依托于領(lǐng)域服務(wù)。
舉例
必須通過(guò)訂單領(lǐng)域服務(wù)來(lái)創(chuàng)建和訪問(wèn)訂單
領(lǐng)域事件
領(lǐng)域事件是對(duì)領(lǐng)域內(nèi)發(fā)生的活動(dòng)進(jìn)行的建模。捕獲一些有價(jià)值的領(lǐng)域活動(dòng)事件。
作用
- 解耦:可以通過(guò)發(fā)布訂閱模式,發(fā)布領(lǐng)域事件
- 一致性:通過(guò)領(lǐng)域事件來(lái)達(dá)到最終一致性
- 事件溯源
舉例
發(fā)送聊天消息,這屬于一個(gè)領(lǐng)域事件;撤回消息,也屬于一個(gè)領(lǐng)域事件。
推送服務(wù)訂閱消息事件,然后將消息推送給用戶端。這樣就解耦了消息服務(wù)與推送服務(wù)之間的強(qiáng)依賴關(guān)系。
資源庫(kù)Repository
資源庫(kù)用于保存和獲取聚合對(duì)象。
領(lǐng)域模型 vs 數(shù)據(jù)模型
資源庫(kù)介于領(lǐng)域模型(業(yè)務(wù)模型)和數(shù)據(jù)模型(數(shù)據(jù)庫(kù))之間,主要用于聚合對(duì)象的持久化和檢索。
資源庫(kù)隔離了領(lǐng)域模型和數(shù)據(jù)模型,以便上層只需要關(guān)注于領(lǐng)域模型而不需要考慮如何進(jìn)行持久化。
分層架構(gòu)
把一系列相同的對(duì)象進(jìn)行分類放在同一層,然后根據(jù)他們之間的依賴關(guān)系再確定上下層次關(guān)系。
在實(shí)際決策時(shí),我們需要知道各層的職責(zé)、意義以及相應(yīng)的場(chǎng)景;
落實(shí)到代碼層面時(shí),我們還需要知道各層所包含的具體內(nèi)容、各層的一些常見(jiàn)的具體策略/模式、層次之間的交互/依賴關(guān)系。
DDD經(jīng)典分層架構(gòu)
- 用戶接口層(interfaces):處理顯示和用戶請(qǐng)求,以及一些基本的參數(shù)檢查,不包括業(yè)務(wù)邏輯
- 應(yīng)用層(application):主要協(xié)調(diào)領(lǐng)域?qū)ο蟮牟僮?;處理持久化事?wù)、發(fā)送消息、安全認(rèn)證等
- 領(lǐng)域?qū)樱╠omain):處理核心業(yè)務(wù)邏輯,不包括技術(shù)實(shí)現(xiàn)細(xì)節(jié)。領(lǐng)域?qū)邮菢I(yè)務(wù)軟件的核心
- 基礎(chǔ)設(shè)施層(infrastructure):處理純技術(shù)細(xì)節(jié),為其他層提供技術(shù)支撐,也可用于封裝調(diào)用的外部系統(tǒng)細(xì)節(jié)。例如:持久化的實(shí)現(xiàn),消息中間件的實(shí)現(xiàn),工具類,rpc等
個(gè)人理解:這種分層,既可以在一個(gè)單體應(yīng)用中,也可以是微服務(wù)的形式。DDD分層并不一定要按微服務(wù)的服務(wù)粒度進(jìn)行分層。
如果一個(gè)業(yè)務(wù)邏輯非常簡(jiǎn)單的子域,則可以將幾層都放進(jìn)一個(gè)單體應(yīng)用中,在應(yīng)用中進(jìn)行分層。如果業(yè)務(wù)較為復(fù)雜,則可以按服務(wù)進(jìn)行拆分,每層都有自己對(duì)應(yīng)的服務(wù)。
其他架構(gòu)
- 對(duì)稱性架構(gòu)
- 洋蔥架構(gòu)
- 整潔架構(gòu)
- CQRS架構(gòu)
DDD工程實(shí)踐
以一個(gè)簡(jiǎn)化的社交領(lǐng)域的例子來(lái)實(shí)踐DDD。
核心概念
- 用戶(User): 一個(gè)賬戶,并以用戶id識(shí)別
- 關(guān)系(Relationship):用戶之間的關(guān)系
- 動(dòng)態(tài)(Feed): 用戶發(fā)布文字,圖片,視頻,評(píng)論等內(nèi)容
- 會(huì)話(Conversation):用戶之間的聊天會(huì)話
領(lǐng)域設(shè)計(jì)
戰(zhàn)略建模
領(lǐng)域就是社交領(lǐng)域,核心問(wèn)題和絕大部分社交系統(tǒng)一樣。
子域
- 核心域:聊天,動(dòng)態(tài)
- 支撐子域:反作弊,推廣
- 通用子域:用戶,關(guān)系,消息推送
上下文
- 消息上下文
- 會(huì)話上下文
- 動(dòng)態(tài)上下文
- 推送上下文
- 用戶上下文
戰(zhàn)術(shù)建模
以會(huì)話上下文為例子來(lái)進(jìn)行戰(zhàn)術(shù)建模
會(huì)話上下文
- 會(huì)話:聚合根用戶:實(shí)體用戶:實(shí)體消息列表:實(shí)體發(fā)送人:實(shí)體接收人:實(shí)體消息內(nèi)容:值對(duì)象
消息在會(huì)話上下文屬于實(shí)體,在消息上下文屬于聚合根。
結(jié)構(gòu)
以會(huì)話子域?yàn)槔?/p>
架構(gòu)分層
- interfaces 接口層RESTfulRPC
- application 應(yīng)用層Conversationmessage
- domain_service 領(lǐng)域服務(wù)層modelConversationMessagerepositoryConversationMessage
- infrastructure 基礎(chǔ)設(shè)施層storeConversationRepositoryMessageRepositorymessageSendMessageutils
領(lǐng)域
package domain// 聚合根type Conversation struct { ID int User1 User User2 User Messages list.List}// 實(shí)體type Message struct { ID int From User // 實(shí)體 To User Body Content // 值對(duì)象}
用戶接口層
type ChatInferface struct { // 應(yīng)用層 app app.ChatApplication}func (c *ChatInferface) Route() { c.route("POST", "/api/message", c.SendMessage) c.route("PATCH", "/api/message", c.RecallMessage)}// POST /api/messagefunc (c *ChatInferface) SendMessage(ctx *Context) { if !c.validateRequest(ctx) { return } message := c.parseMessage(ctx) app.SendMessage(message)}func (c *ChatInferface) RecallMessage(ctx *Context) { if !c.validateRequest(ctx) { return } messageID := c.parseMessage(ctx) app.RecallMessage(messageID)}
應(yīng)用層
type ChatApplication struct { user service.UserService chat service.ChatService // 這里領(lǐng)域事件由應(yīng)用層發(fā)布 // publisher EventPublisher lbs LBSFacade}func (c *ChatApplication) SendMessage(msg *Message) { if !c.user.CheckUser(msg.UserID) { return } c.chat.SendMessage(msg)}
領(lǐng)域服務(wù)層
type ChatService struct { // 領(lǐng)域事件 publisher MessageEventPublisher repo MessageRepository}func (c *ChatService SendMessage(msg *Message) { // 業(yè)務(wù)邏輯 ... // 領(lǐng)域資源持久化 c.repo.Save(msg) // 發(fā)布領(lǐng)域事件 c.publisher.Publish(msg)}
基礎(chǔ)設(shè)施層
package infrastructuretype MessageRepository struct { db MessageDatabase cache MessageCache}func (m *MessageRepository) Save(msg *Message) { db.Save(m.ToPO(msg))}func (m MessageRepository) Get(msgID int) *Message { msg := m.cache.Get(msgID) if msg != nil { return m.FromPO(msg) } return m.FromPO(m.db.Get(msgID))}
總結(jié)
在設(shè)計(jì)和實(shí)現(xiàn)一個(gè)系統(tǒng)的時(shí)候,這個(gè)系統(tǒng)所要處理問(wèn)題的領(lǐng)域?qū)<液烷_(kāi)發(fā)人員以一套統(tǒng)一語(yǔ)言進(jìn)行協(xié)作,共同完成該領(lǐng)域模型的構(gòu)建,在這個(gè)過(guò)程中,業(yè)務(wù)架構(gòu)和系統(tǒng)架構(gòu)等問(wèn)題都得到了解決,之后將領(lǐng)域模型中關(guān)于系統(tǒng)架構(gòu)的主體映射為實(shí)現(xiàn)代碼,完成系統(tǒng)的實(shí)現(xiàn)落地。