DDD 是什么?—— 你以前只會(huì)用 Service + 貧血模型!
大家好,我是技術(shù)UP主小傅哥。
DDD 是什么,這應(yīng)該是每個(gè)想使用 DDD 開發(fā)項(xiàng)目的研發(fā)伙伴,遇到的第一個(gè)疑問,只有搞清楚它到底是什么才好上手使用。而 DDD 既不是 MVC 一樣的工程結(jié)構(gòu),也不能直接等同于微服務(wù)架構(gòu),更不是一種設(shè)計(jì)模式。
1. DDD 是什么
那 DDD 是什么呢?來自于維基百科的一段定義:"Domain-driven design (DDD) is a major software design approach. ",DDD 是一種軟件設(shè)計(jì)方法。也就是說 DDD 是指導(dǎo)我們做軟件工程設(shè)計(jì)的一種手段,它提供了用切割工程模型的各類技巧,如;領(lǐng)域、界限上下文、實(shí)體、值對(duì)象、聚合、工廠、倉儲(chǔ)等。通過 DDD 的指導(dǎo)思想,我們可以在前期投入更多的時(shí)間,更加合理的規(guī)劃出可持續(xù)迭代的工程設(shè)計(jì)。
在 DDD 中有一套共識(shí)的工程兩階段設(shè)計(jì)手段,包括;戰(zhàn)略設(shè)計(jì)、戰(zhàn)術(shù)設(shè)計(jì)。
- 戰(zhàn)略設(shè)計(jì),主要以應(yīng)對(duì)復(fù)雜的業(yè)務(wù)需求,通過抽象、分治的過程,合理的拆分為獨(dú)立的多個(gè)微服務(wù),從而分而治之。與之評(píng)價(jià)拆分的是否合理,則是在需求開發(fā)上線時(shí)候,是否每次都大量操作多個(gè)微服務(wù)開發(fā)和上線。這樣的戰(zhàn)略設(shè)計(jì)是一種失敗的微服務(wù)單體設(shè)計(jì)。所以少數(shù)幾個(gè)中等規(guī)模的單體應(yīng)用,周圍環(huán)繞著一個(gè)服務(wù)生態(tài)系統(tǒng),這更有意義。你實(shí)際上并沒有構(gòu)建微服務(wù) @賈斯汀·埃瑟里奇
- 戰(zhàn)術(shù)設(shè)計(jì),在這個(gè)范疇下,主要以討論如何基于面向?qū)ο笏季S,運(yùn)用領(lǐng)域模型來表達(dá)業(yè)務(wù)概念。通常在不做領(lǐng)域模型設(shè)計(jì)的架構(gòu),也就是通常映射到 MVC 三層架構(gòu)下,Service + 數(shù)據(jù)模型的開發(fā)模式,會(huì)讓 Service 扁平的、大量的,平鋪出非常復(fù)雜的業(yè)務(wù)邏輯代碼。再加上行為對(duì)象與功能邏輯的分離,貧血模型的開發(fā)方式,讓行為對(duì)象的不斷交叉使用,也是讓系統(tǒng)不斷增加復(fù)雜度,并到難以維護(hù)的根因。所以這一階段要設(shè)計(jì)每一個(gè)可以表達(dá)領(lǐng)域概念的模型,并運(yùn)用實(shí)體、聚合、領(lǐng)域服務(wù)來承載。
2. DDD 的概念
什么是充血模型,領(lǐng)域內(nèi)都包括什么,實(shí)體、聚合、值對(duì)象,有什么區(qū)別?這樣一些"為什么"的概念,也是戰(zhàn)術(shù)設(shè)計(jì)過程中非常重要的知識(shí)項(xiàng)。搞清楚它們才能做 DDD 設(shè)計(jì)。
2.1 充血模型
充血模型,指將對(duì)象的屬性信息與行為邏輯聚合到一個(gè)類中,常用的手段如在對(duì)象內(nèi)提供屬于當(dāng)前對(duì)象的信息校驗(yàn)、拼裝緩存Key、不含服務(wù)接口調(diào)用的邏輯處理等。
圖片
- 這樣的方式可以在使用一個(gè)對(duì)象時(shí),就順便拿到這個(gè)對(duì)象的提供的一系列方法信息,所有使用對(duì)象的邏輯方法,都不需要自己再次處理同類邏輯。
- 但不要只是把充血模型,僅限于一個(gè)類的設(shè)計(jì)和一個(gè)類內(nèi)的方法設(shè)計(jì)。充血還可以是整個(gè)包結(jié)構(gòu),一個(gè)包下包括了用于實(shí)現(xiàn)此包 Service 服務(wù)所需的各類零部件(模型、倉儲(chǔ)、工廠),也可以被看做充血模型。
- 同時(shí)我們還會(huì)再一個(gè)同類的類下,提供對(duì)應(yīng)的內(nèi)部類,如用戶實(shí)名,包括了,通信類、實(shí)名卡、銀行卡、四要素等。它們都被寫進(jìn)到一個(gè)用戶類下的內(nèi)部子類,這樣在代碼編寫中也會(huì)清晰的看到子類的所屬信息,更容易理解代碼邏輯,也便于維護(hù)迭代。
2.2 領(lǐng)域模型
領(lǐng)域模型,指特定業(yè)務(wù)領(lǐng)域內(nèi),業(yè)務(wù)規(guī)則、策略以及業(yè)務(wù)流程的抽象和封裝。在設(shè)計(jì)手段上,通過風(fēng)暴模型拆分領(lǐng)域模塊,形成界限上下文。最大的區(qū)別在于把原有的眾多 Service + 數(shù)據(jù)模型的方式,拆分為獨(dú)立的有邊界的領(lǐng)域模塊。每個(gè)領(lǐng)域內(nèi)創(chuàng)建自身所屬的;領(lǐng)域?qū)ο螅▽?shí)體、聚合、值對(duì)象)、倉儲(chǔ)服務(wù)(DAO 操作)、工廠、端口適配器Port(調(diào)用外部接口的手段)等。
那么,現(xiàn)在這里有幾個(gè)概念;領(lǐng)域服務(wù)、領(lǐng)域?qū)ο蟆}儲(chǔ)定義、事件消息、端口適配器。我們先來看他們是怎么從貧血模型演變過來的,在細(xì)分講解每個(gè)概念。
圖片
- 在原本的 Service + 貧血的數(shù)據(jù)模型開發(fā)指導(dǎo)下,Service 串聯(lián)調(diào)用每一個(gè)功能模塊。這些基礎(chǔ)設(shè)施(對(duì)象、方法、接口)是被相互掉調(diào)用的。這也是因?yàn)樨氀P筒]有面向?qū)ο蟮脑O(shè)計(jì),所有的需求開發(fā)只有詳細(xì)設(shè)計(jì)。
- 換到充血模型下,現(xiàn)在我們以一個(gè)領(lǐng)域功能為聚合,拆分一個(gè)領(lǐng)域內(nèi)所需的 Service 為領(lǐng)域服務(wù),VO、Req、Res 重新設(shè)計(jì)為領(lǐng)域?qū)ο?,DAO、Redis 等持久化操作為倉儲(chǔ)等。舉例;一套賬戶服務(wù)中的,授信認(rèn)證、開戶、提額降額等,每一個(gè)都是一個(gè)獨(dú)立的領(lǐng)域,在每個(gè)獨(dú)立的領(lǐng)域內(nèi),創(chuàng)建自身領(lǐng)域所需的各項(xiàng)信息。
- 領(lǐng)域模型還有一個(gè)特點(diǎn),它自身只關(guān)注業(yè)務(wù)功能實(shí)現(xiàn),不與外部任何接口和服務(wù)直連。如;不會(huì)直接調(diào)用 DAO 操作庫,也不會(huì)調(diào)用緩存操作 Redis,更不會(huì)直接引入 RPC 連接其他微服務(wù)。而是通過倉庫和端口適配器,定義調(diào)用外部數(shù)據(jù)的含有出入?yún)?duì)象的接口標(biāo)準(zhǔn),讓基礎(chǔ)設(shè)施層做具體的調(diào)用實(shí)現(xiàn)。通過這樣的方式讓領(lǐng)域只關(guān)心業(yè)務(wù)實(shí)現(xiàn),同時(shí)做好防腐。
2.3 實(shí)體、聚合、值對(duì)象
原本在貧血模型下的開發(fā),常常是不會(huì)特別在意一個(gè)方法的出入?yún)?duì)象的,也經(jīng)常是很多個(gè)服務(wù)共用一個(gè)VO對(duì)象作為入?yún)?,只要這個(gè)對(duì)象能把我需要的屬性信息帶進(jìn)來就可以了。
但在 DDD 的領(lǐng)域模型設(shè)計(jì)下,領(lǐng)域?qū)ο蟮脑O(shè)計(jì)是非常面向?qū)ο蟮?。而且在整個(gè)風(fēng)暴事件的四色建模過程也是在以領(lǐng)域?qū)ο鬄轵?qū)動(dòng)進(jìn)行的。
實(shí)體、聚合、值對(duì)象,三者位于每個(gè)領(lǐng)域下的領(lǐng)域?qū)ο髢?nèi),服務(wù)于領(lǐng)域內(nèi)的領(lǐng)域服務(wù)。三個(gè)對(duì)象定義具體如下;
圖片
實(shí)體
是依托于持久化層數(shù)據(jù)以領(lǐng)域服務(wù)功能目標(biāo)為指導(dǎo)設(shè)計(jì)的領(lǐng)域?qū)ο?。持久化PO對(duì)象是原子類對(duì)象,不具有業(yè)務(wù)語義,而實(shí)體對(duì)象是具有業(yè)務(wù)語義且有唯一標(biāo)識(shí)的對(duì)象,跟隨于領(lǐng)域服務(wù)方法的全生命周期對(duì)象。如;用戶PO持久化對(duì)象,會(huì)涵蓋,用戶的開戶實(shí)體、授信實(shí)體、額度實(shí)體對(duì)象。也包括如商品下單時(shí)候的購物車實(shí)體對(duì)象。這個(gè)對(duì)象也通常是領(lǐng)域服務(wù)方法的入?yún)?duì)象。
- 概念:實(shí)體 = 唯一標(biāo)識(shí) + 狀態(tài)屬性 + 行為動(dòng)作(功能),是DDD中的一個(gè)基本構(gòu)建塊,它代表了具有唯一標(biāo)識(shí)的領(lǐng)域?qū)ο?。?shí)體不僅僅包含數(shù)據(jù)(狀態(tài)屬性),還包含了相關(guān)的行為(功能),并且它的標(biāo)識(shí)在整個(gè)生命周期中保持不變。
- 特征:
唯一標(biāo)識(shí):實(shí)體具有一個(gè)可以區(qū)分其他實(shí)體的標(biāo)識(shí)符。這個(gè)標(biāo)識(shí)符可以是一個(gè)ID、一個(gè)復(fù)合鍵或者是一個(gè)自然鍵,關(guān)鍵是它能夠唯一地標(biāo)識(shí)實(shí)體實(shí)例。
領(lǐng)域標(biāo)識(shí):實(shí)體的標(biāo)識(shí)通常來源于業(yè)務(wù)領(lǐng)域,例如用戶ID、訂單ID等。這些標(biāo)識(shí)符在業(yè)務(wù)上有特定的含義,并且在系統(tǒng)中是唯一的。
委派標(biāo)識(shí):在某些情況下,實(shí)體的標(biāo)識(shí)可能是由ORM(對(duì)象關(guān)系映射)框架自動(dòng)生成的,如數(shù)據(jù)庫中的自增主鍵。這種標(biāo)識(shí)符雖然可以唯一標(biāo)識(shí)實(shí)體,但它并不直接來源于業(yè)務(wù)領(lǐng)域。
- 用途:
表達(dá)業(yè)務(wù)概念:實(shí)體用于在軟件中表達(dá)具體的業(yè)務(wù)概念,如用戶、訂單、交易等。通過實(shí)體的屬性和行為,可以描述這些業(yè)務(wù)對(duì)象的特征和能力。
封裝業(yè)務(wù)邏輯:實(shí)體不僅僅承載數(shù)據(jù),還封裝了業(yè)務(wù)規(guī)則和邏輯。這些邏輯包括驗(yàn)證數(shù)據(jù)的有效性、執(zhí)行業(yè)務(wù)規(guī)則、計(jì)算屬性值等。這樣做的目的是保證業(yè)務(wù)邏輯的集中和一致性。
保持?jǐn)?shù)據(jù)一致性:實(shí)體負(fù)責(zé)維護(hù)自身的狀態(tài)和數(shù)據(jù)一致性。它確保自己的屬性和關(guān)聯(lián)關(guān)系在任何時(shí)候都是正確和完整的,從而避免數(shù)據(jù)的不一致性。
實(shí)現(xiàn)手段:
定義實(shí)體類:在代碼中定義一個(gè)類,該類包含實(shí)體的屬性、構(gòu)造函數(shù)、方法等。
實(shí)現(xiàn)唯一標(biāo)識(shí):為實(shí)體類提供一個(gè)唯一標(biāo)識(shí)的屬性,如ID,并確保在實(shí)體的生命周期中這個(gè)標(biāo)識(shí)保持不變。
封裝行為:在實(shí)體類中實(shí)現(xiàn)業(yè)務(wù)邏輯的方法,這些方法可以操作實(shí)體的狀態(tài),并執(zhí)行相關(guān)的業(yè)務(wù)規(guī)則。
使用ORM框架:利用ORM框架將實(shí)體映射到數(shù)據(jù)庫表中,這樣可以簡(jiǎn)化數(shù)據(jù)持久化的操作。
實(shí)現(xiàn)領(lǐng)域服務(wù):對(duì)于跨實(shí)體或跨聚合的操作,可以實(shí)現(xiàn)領(lǐng)域服務(wù)來處理這些操作,而不是在實(shí)體中直接實(shí)現(xiàn)。
使用領(lǐng)域事件:當(dāng)實(shí)體的狀態(tài)發(fā)生變化時(shí),可以發(fā)布領(lǐng)域事件,這樣可以通知其他部分的系統(tǒng)進(jìn)行相應(yīng)的處理。
值對(duì)象
這個(gè)對(duì)象在領(lǐng)域服務(wù)方法的生命周期過程內(nèi)是不可變對(duì)象,也沒有唯一標(biāo)識(shí)。它通常是配合實(shí)體對(duì)象使用。如為實(shí)體對(duì)象提供對(duì)象屬性值的描述,比如;一個(gè)公司雇員的級(jí)別值對(duì)象,一個(gè)下單的商品收貨的四級(jí)地址信息對(duì)象。所以在開發(fā)值對(duì)象的時(shí)候,通常不會(huì)提供 setter 方法,而是提供構(gòu)造函數(shù)或者 Builder 方法來實(shí)例化對(duì)象。這個(gè)對(duì)象通常不會(huì)獨(dú)立作為方法的入?yún)?duì)象,但做可以獨(dú)立作為出參對(duì)象使用。
- 概念:值對(duì)象是由一組屬性組成的,它們共同描述了一個(gè)領(lǐng)域概念。與實(shí)體(Entity)不同,值對(duì)象不需要有一個(gè)唯一的標(biāo)識(shí)符來區(qū)分它們。值對(duì)象通常是不可變的,這意味著一旦創(chuàng)建,它們的狀態(tài)就不應(yīng)該改變。
- 特征:
不可變性(Immutability):值對(duì)象一旦被創(chuàng)建,它的狀態(tài)就不應(yīng)該發(fā)生變化。這有助于保證領(lǐng)域模型的一致性和線程安全性。
等價(jià)性(Equality):值對(duì)象的等價(jià)性不是基于身份或引用,而是基于對(duì)象的屬性值。如果兩個(gè)值對(duì)象的所有屬性值都相等,那么這兩個(gè)對(duì)象就被認(rèn)為是等價(jià)的。
替換性(Replaceability):由于值對(duì)象是不可變的,任何需要改變值對(duì)象的操作都會(huì)導(dǎo)致創(chuàng)建一個(gè)新的值對(duì)象實(shí)例,而不是修改現(xiàn)有的實(shí)例。
側(cè)重于描述事物的狀態(tài):值對(duì)象通常用來描述事物的狀態(tài),而不是事物的唯一身份。
可復(fù)用性(Reusability):值對(duì)象可以在不同的領(lǐng)域?qū)嶓w或其他值對(duì)象中重復(fù)使用。
- 用途:
金額和貨幣(如價(jià)格、工資、費(fèi)用等)
度量和數(shù)據(jù)(如重量、長(zhǎng)度、體積等)
范圍或區(qū)間(如日期范圍、溫度區(qū)間等)
復(fù)雜的數(shù)學(xué)模型(如坐標(biāo)、向量等)
任何其他需要封裝的屬性集合
實(shí)現(xiàn)手段:
定義不可變類:確保類的所有屬性都是私有的,并且只能通過構(gòu)造函數(shù)來設(shè)置。
重寫equals和hashCode方法:這樣可以確保值對(duì)象的等價(jià)性是基于它們的屬性值,而不是對(duì)象的引用。
提供只讀訪問器:只提供獲取屬性值的方法,不提供修改屬性值的方法。
使用工廠方法或構(gòu)造函數(shù)創(chuàng)建實(shí)例:這有助于確保值對(duì)象的有效性和一致性。
考慮序列化支持:如果值對(duì)象需要在網(wǎng)絡(luò)上傳輸或存儲(chǔ)到數(shù)據(jù)庫中,需要提供序列化和反序列化的支持。
聚合
當(dāng)你對(duì)數(shù)據(jù)庫的操作需要使用到多個(gè)實(shí)體時(shí),可以創(chuàng)建聚合對(duì)象。一個(gè)聚合對(duì)象,代表著一個(gè)數(shù)據(jù)庫事務(wù),具有事務(wù)一致性。聚合中的實(shí)體可以由聚合提供創(chuàng)建操作,實(shí)體也被稱為聚合根對(duì)象。一個(gè)訂單的聚合,會(huì)涵蓋;下單用戶實(shí)體對(duì)象、訂單實(shí)體、訂單明細(xì)實(shí)體和訂單收貨四級(jí)地址值對(duì)象。而那個(gè)作為入?yún)⒌馁徫镘噷?shí)體對(duì)象,已經(jīng)被轉(zhuǎn)換為實(shí)體對(duì)象了?!?聚合內(nèi)事務(wù)一致性,聚合外最終一致性。
- 概念:聚合是領(lǐng)域模型中的一個(gè)關(guān)鍵概念,它是一組具有內(nèi)聚性的相關(guān)對(duì)象的集合,這些對(duì)象一起工作以執(zhí)行某些業(yè)務(wù)規(guī)則或操作。聚合定義了一組對(duì)象的邊界,這些對(duì)象可以被視為一個(gè)單一的單元進(jìn)行處理。
- 特征:
一致性邊界:聚合確保其內(nèi)部對(duì)象的狀態(tài)變化是一致的。當(dāng)對(duì)聚合內(nèi)的對(duì)象進(jìn)行操作時(shí),這些操作必須保持聚合內(nèi)所有對(duì)象的一致性。
根實(shí)體:每個(gè)聚合都有一個(gè)根實(shí)體(Aggregate Root),它是聚合的入口點(diǎn)。根實(shí)體擁有一個(gè)全局唯一的標(biāo)識(shí)符,其他對(duì)象通過根實(shí)體與聚合交互。
事務(wù)邊界:聚合也定義了事務(wù)的邊界。在聚合內(nèi)部,所有的變更操作應(yīng)該是原子的,即它們要么全部成功,要么全部失敗,以此來保證數(shù)據(jù)的一致性。
- 用途:
封裝業(yè)務(wù)邏輯:聚合通過將相關(guān)的對(duì)象和操作封裝在一起,提供了一個(gè)清晰的業(yè)務(wù)邏輯模型,有助于業(yè)務(wù)規(guī)則的實(shí)施和維護(hù)。
保證一致性:聚合確保內(nèi)部狀態(tài)的一致性,通過定義清晰的邊界和規(guī)則,聚合可以在內(nèi)部強(qiáng)制執(zhí)行業(yè)務(wù)規(guī)則,從而保證數(shù)據(jù)的一致性。
簡(jiǎn)化復(fù)雜性:聚合通過組織相關(guān)的對(duì)象,簡(jiǎn)化了領(lǐng)域模型的復(fù)雜性。這有助于開發(fā)者更好地理解和擴(kuò)展系統(tǒng)。
實(shí)現(xiàn)手段:
定義聚合根:選擇合適的聚合根是實(shí)現(xiàn)聚合的第一步。聚合根應(yīng)該是能夠代表整個(gè)聚合的實(shí)體,并且擁有唯一標(biāo)識(shí)。
限制訪問路徑:只能通過聚合根來修改聚合內(nèi)的對(duì)象,不允許直接修改聚合內(nèi)部對(duì)象的狀態(tài),以此來維護(hù)邊界和一致性。
設(shè)計(jì)事務(wù)策略:在聚合內(nèi)部實(shí)現(xiàn)事務(wù)一致性,確保操作要么全部完成,要么全部回滾。對(duì)于聚合之間的交互,可以采用領(lǐng)域事件或其他機(jī)制來實(shí)現(xiàn)最終一致性。
封裝業(yè)務(wù)規(guī)則:在聚合內(nèi)部實(shí)現(xiàn)業(yè)務(wù)規(guī)則和邏輯,確保所有的業(yè)務(wù)操作都遵循這些規(guī)則。
持久化:聚合根通常與數(shù)據(jù)持久化層交互,以保存聚合的狀態(tài)。這通常涉及到對(duì)象-關(guān)系映射(ORM)或其他數(shù)據(jù)映射技術(shù)。
2.4 倉儲(chǔ)和適配器
在 DDD 的設(shè)計(jì)方法中,領(lǐng)域?qū)幼龅搅酥魂P(guān)心領(lǐng)域服務(wù)實(shí)現(xiàn)。最能體現(xiàn)這樣設(shè)計(jì)的就是倉庫和適配器的設(shè)計(jì)。通常在 Service + 數(shù)據(jù)模型的設(shè)計(jì)中,會(huì)在 Service 中引入 Redis、RPC、配置中心等各類其他外部服務(wù)。但在 DDD 中,通過倉儲(chǔ)和適配器以及基礎(chǔ)設(shè)施層的定義,解耦了這部分內(nèi)容。
圖片
- 特征:
封裝持久化操作:Repository負(fù)責(zé)封裝所有與數(shù)據(jù)源交互的操作,如創(chuàng)建、讀取、更新和刪除(CRUD)操作。這樣,領(lǐng)域?qū)拥拇a就可以避免直接處理數(shù)據(jù)庫或其他存儲(chǔ)機(jī)制的復(fù)雜性。
領(lǐng)域?qū)ο蟮募瞎芾恚篟epository通常被視為領(lǐng)域?qū)ο蟮募?,提供了查詢和過濾這些對(duì)象的方法,使得領(lǐng)域?qū)ο蟮墨@取和管理更加方便。
抽象接口:Repository定義了一個(gè)與持久化機(jī)制無關(guān)的接口,這使得領(lǐng)域?qū)拥拇a可以在不同的持久化機(jī)制之間切換,而不需要修改業(yè)務(wù)邏輯。
- 用途:
數(shù)據(jù)訪問抽象:Repository為領(lǐng)域?qū)犹峁┝艘粋€(gè)清晰的數(shù)據(jù)訪問接口,使得領(lǐng)域?qū)ο罂梢詫W⒂跇I(yè)務(wù)邏輯的實(shí)現(xiàn),而不是數(shù)據(jù)訪問的細(xì)節(jié)。
領(lǐng)域?qū)ο蟮牟樵兒凸芾恚篟epository使得對(duì)領(lǐng)域?qū)ο蟮牟樵兒凸芾碜兊酶臃奖愫挽`活,支持復(fù)雜的查詢邏輯。
領(lǐng)域邏輯與數(shù)據(jù)存儲(chǔ)分離:通過Repository模式,領(lǐng)域邏輯與數(shù)據(jù)存儲(chǔ)邏輯分離,提高了領(lǐng)域模型的純粹性和可測(cè)試性。
優(yōu)化數(shù)據(jù)訪問:Repository實(shí)現(xiàn)可以包含數(shù)據(jù)訪問的優(yōu)化策略,如緩存、批處理操作等,以提高應(yīng)用程序的性能。
實(shí)現(xiàn)手段:
定義Repository接口:在領(lǐng)域?qū)佣x一個(gè)或多個(gè)Repository接口,這些接口聲明了所需的數(shù)據(jù)訪問方法。
實(shí)現(xiàn)Repository接口:在基礎(chǔ)設(shè)施層或數(shù)據(jù)訪問層實(shí)現(xiàn)這些接口,具體實(shí)現(xiàn)可能是使用ORM(對(duì)象關(guān)系映射)框架,如MyBatis、Hibernate等,或者直接使用數(shù)據(jù)庫訪問API,如JDBC等。
依賴注入:在應(yīng)用程序中使用依賴注入(DI)來將具體的Repository實(shí)現(xiàn)注入到需要它們的領(lǐng)域服務(wù)或應(yīng)用服務(wù)中。這樣做可以進(jìn)一步解耦領(lǐng)域?qū)雍蛿?shù)據(jù)訪問層,同時(shí)也便于單元測(cè)試。
使用規(guī)范模式(Specification Pattern):有時(shí)候,為了構(gòu)建復(fù)雜的查詢,可以結(jié)合使用規(guī)范模式,這是一種允許將業(yè)務(wù)規(guī)則封裝為單獨(dú)的業(yè)務(wù)邏輯單元的模式,這些單元可以被Repository用來構(gòu)建查詢。
Repository模式是DDD(領(lǐng)域驅(qū)動(dòng)設(shè)計(jì))中的一個(gè)核心概念,它有助于保持領(lǐng)域模型的聚焦和清晰,同時(shí)提供了靈活、可測(cè)試和可維護(hù)的數(shù)據(jù)訪問策略。
倉儲(chǔ)解耦的手段使用了依賴倒置的設(shè)計(jì),所有領(lǐng)域需要的外部服務(wù),不在直接引入外部的服務(wù),而是通過定義接口的方式,讓基礎(chǔ)設(shè)施層實(shí)現(xiàn)領(lǐng)域?qū)咏涌冢▊}儲(chǔ)/適配器)的方式來處理。
那么也就是基礎(chǔ)設(shè)置層負(fù)責(zé)原則對(duì)接數(shù)據(jù)庫、緩存、配置中心、RPC接口、HTTP接口、MQ推送等各項(xiàng)資源,并承接領(lǐng)域服務(wù)的接口調(diào)用各項(xiàng)服務(wù)為領(lǐng)域?qū)犹峁?shù)據(jù)能力。
同時(shí)這也會(huì)體現(xiàn)出,領(lǐng)域?qū)拥膶?shí)現(xiàn)是具有業(yè)務(wù)語義的,而到了基礎(chǔ)設(shè)置層則沒有業(yè)務(wù)語義,都是原子的方法。通過原子方法的組合為領(lǐng)域業(yè)務(wù)語義提供支撐。
2.5 領(lǐng)域編排
在 DDD 中,每一個(gè)領(lǐng)域都是界限上下文拆分的獨(dú)立結(jié)果,而實(shí)現(xiàn)業(yè)務(wù)流程的功能則需要串聯(lián)各個(gè)領(lǐng)域模塊提供一整條鏈路的完整服務(wù)。所以也常說領(lǐng)域內(nèi)事務(wù)一致性,領(lǐng)域外最終一致性。
同時(shí)這些領(lǐng)域模塊因?yàn)槭仟?dú)立的,所以也可以被復(fù)用。在不同的場(chǎng)景功能訴求下,可以選擇不同的領(lǐng)域模塊進(jìn)行組裝,這個(gè)過程就像搭積木一樣。
但這里有一個(gè)取舍,如果項(xiàng)目相對(duì)來說并不大,也沒有太多的編排處理。那么可以直接讓觸發(fā)器層對(duì)接領(lǐng)域?qū)?,減少編排層后,編碼會(huì)更加便捷。
2.6 觸發(fā)器
在所有的模型都定義完成后,領(lǐng)域業(yè)務(wù)被串聯(lián)了。那么接下來則是使用,而使用的方式可以包括;接口(http/rpc)、消息監(jiān)聽、定時(shí)任務(wù)等方式,這些方式統(tǒng)一被定義為觸發(fā)動(dòng)作。
由觸發(fā)發(fā)起對(duì)編排功能的調(diào)用處理。如;定時(shí)任務(wù)做信貸的計(jì)息、開戶成功消息通知返利優(yōu)惠券、提供接口讓外部調(diào)用授信邏輯等。這些都是觸發(fā)動(dòng)作。