欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

DDD框架落地實(shí)戰(zhàn)

 更新時(shí)間:2022年08月05日 08:38:16   作者:沉默王二  
這篇文章主要為大家介紹了DDD框架落地實(shí)戰(zhàn)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

1. 走進(jìn) DDD

1.1 為什么要用 DDD ?

  • 面向?qū)ο笤O(shè)計(jì),數(shù)據(jù)行為綁定,告別貧血模型;
  • 降低復(fù)雜度,分而治之;
  • 優(yōu)先考慮領(lǐng)域模型,而不是切割數(shù)據(jù)和行為;
  • 準(zhǔn)確傳達(dá)業(yè)務(wù)規(guī)則,業(yè)務(wù)優(yōu)先;
  • 代碼即設(shè)計(jì);
  • 它通過邊界劃分將復(fù)雜業(yè)務(wù)領(lǐng)域簡(jiǎn)單化,幫我們?cè)O(shè)計(jì)出清晰的領(lǐng)域和應(yīng)用邊界,可以很容易地實(shí)現(xiàn)業(yè)務(wù)和技術(shù)統(tǒng)一的架構(gòu)演進(jìn);
  • 領(lǐng)域知識(shí)共享,提升協(xié)助效率;
  • 增加可維護(hù)性和可讀性,延長(zhǎng)軟件生命周期;
  • 中臺(tái)化的基石。

1.2 DDD 作用

說到 DDD,繞不開 MVC,在 MVC 三層架構(gòu)中,我們進(jìn)行功能開發(fā)的之前,拿到需求,解讀需求。往往最先做的一步就是先設(shè)計(jì)表結(jié)構(gòu),在逐層設(shè)計(jì)上層 dao,service,controller。對(duì)于產(chǎn)品或者用戶的需求都做了一層自我理解的轉(zhuǎn)化。

用戶需求在被提出之后經(jīng)過這么多層的轉(zhuǎn)化后,特別是研發(fā)需求在數(shù)據(jù)庫(kù)結(jié)構(gòu)這一層轉(zhuǎn)化后,將業(yè)務(wù)以主觀臆斷行為進(jìn)行了轉(zhuǎn)化。一旦業(yè)務(wù)邊界劃分模糊,考慮不全,大量的邏輯補(bǔ)充堆積到了代碼層實(shí)現(xiàn),變得越來越難維護(hù)。

假如我們現(xiàn)在要做一個(gè)電商訂單下單的需求,涉及到用戶選定商品,下訂單、支付訂單、對(duì)用戶下單時(shí)的訂單發(fā)貨:

  • MVC 架構(gòu):我們常見的做法是在分析好業(yè)務(wù)需求之后,就開始設(shè)計(jì)表結(jié)構(gòu)了,訂單表,支付表,商品表等等。然后編寫業(yè)務(wù)邏輯。這是第一個(gè)版本的需求,功能迭代餓了,訂單支付后我可以取消,下單的商品我們退換貨,是不是又需要進(jìn)行加表,緊跟著對(duì)于的實(shí)現(xiàn)邏輯也進(jìn)行修改。功能不斷迭代,代碼就不斷的層層往上疊。
  • DDD 架構(gòu):我們先進(jìn)行劃分業(yè)務(wù)邊界。這里面核心是訂單。那么訂單就是這個(gè)業(yè)務(wù)領(lǐng)域里面的聚合邏輯體現(xiàn)。支付,商品信息,地址等等都是圍繞著訂單實(shí)體。訂單本身的屬性決定之后,類似于地址只是一個(gè)屬性的體現(xiàn)。當(dāng)你將訂單的領(lǐng)域模型構(gòu)建好之后,后續(xù)的邏輯邊界與倉(cāng)儲(chǔ)設(shè)計(jì)也就隨之而來了。

DDD 整體作用總結(jié)如下:

  • 消除信息不對(duì)稱;
  • 常規(guī) MVC 三層架構(gòu)中自底向上的設(shè)計(jì)方式做一個(gè)反轉(zhuǎn),以業(yè)務(wù)為主導(dǎo),自頂向下的進(jìn)行業(yè)務(wù)領(lǐng)域劃分;
  • 將大的業(yè)務(wù)需求進(jìn)行拆分,分而治之。

2. DDD 架構(gòu)

2.1 DDD 分層架構(gòu)

嚴(yán)格分層架構(gòu):某層只能與直接位于的下層發(fā)生耦合。

松散分層架構(gòu):允許上層與任意下層發(fā)生耦合。

在領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)(DDD)中采用的是松散分層架構(gòu),層間關(guān)系不那么嚴(yán)格。每層都可能使用它下面所有層的服務(wù),而不僅僅是下一層的服務(wù)。每層都可能是半透明的,這意味著有些服務(wù)只對(duì)上一層可見,而有些服務(wù)對(duì)上面的所有層都可見。

分層的作用,從上往下:

  • 用戶交互層:web 請(qǐng)求,rpc 請(qǐng)求,mq 消息等外部輸入均被視為外部輸入的請(qǐng)求,可能修改到內(nèi)部的業(yè)務(wù)數(shù)據(jù)。
  • 業(yè)務(wù)應(yīng)用層:與 MVC 中的 service 不同的不是,service 中存儲(chǔ)著大量業(yè)務(wù)邏輯。但在應(yīng)用服務(wù)的實(shí)現(xiàn)中,它負(fù)責(zé)編排、轉(zhuǎn)發(fā)、校驗(yàn)等。
  • 領(lǐng)域?qū)?/strong>:或稱為模型層,系統(tǒng)的核心,負(fù)責(zé)表達(dá)業(yè)務(wù)概念,業(yè)務(wù)狀態(tài)信息以及業(yè)務(wù)規(guī)則。即包含了該領(lǐng)域所有復(fù)雜的業(yè)務(wù)知識(shí)抽象和規(guī)則定義。該層主要精力要放在領(lǐng)域?qū)ο蠓治錾希梢詮膶?shí)體,值對(duì)象,聚合(聚合根),領(lǐng)域服務(wù),領(lǐng)域事件,倉(cāng)儲(chǔ),工廠等方面入手。
  • 基礎(chǔ)設(shè)施層:主要有 2 方面內(nèi)容,一是為領(lǐng)域模型提供持久化機(jī)制,當(dāng)軟件需要持久化能力時(shí)候才需要進(jìn)行規(guī)劃;一是對(duì)其他層提供通用的技術(shù)支持能力,如消息通信,通用工具,配置等的實(shí)現(xiàn)。

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

2.2 各層數(shù)據(jù)轉(zhuǎn)換

每一層都有自己特定的數(shù)據(jù),可以做如下區(qū)分:

  • VO(View Object):視圖對(duì)象,主要對(duì)應(yīng)界面顯示的數(shù)據(jù)對(duì)象。對(duì)于一個(gè) WEB 頁面,或者 SWT、SWING 的一個(gè)界面,用一個(gè) VO 對(duì)象對(duì)應(yīng)整個(gè)界面的值。
  • DTO(Data Transfer Object):數(shù)據(jù)傳輸對(duì)象,主要用于遠(yuǎn)程調(diào)用等需要大量傳輸對(duì)象的地方。比如我們一張表有 100 個(gè)字段,那么對(duì)應(yīng)的 PO 就有 100 個(gè)屬性。但是我們界面上只要顯示 10 個(gè)字段,客戶端用 WEB service 來獲取數(shù)據(jù),沒有必要把整個(gè) PO 對(duì)象傳遞到客戶端,這時(shí)我們就可以用只有這 10 個(gè)屬性的 DTO 來傳遞結(jié)果到客戶端,這樣也不會(huì)暴露服務(wù)端表結(jié)構(gòu)。到達(dá)客戶端以后,如果用這個(gè)對(duì)象來對(duì)應(yīng)界面顯示,那此時(shí)它的身份就轉(zhuǎn)為 VO。在這里,我泛指用于展示層與服務(wù)層之間的數(shù)據(jù)傳輸對(duì)象。
  • DO(Domain Object):領(lǐng)域?qū)ο?,就是從現(xiàn)實(shí)世界中抽象出來的有形或無形的業(yè)務(wù)實(shí)體。
  • PO(Persistent Object):持久化對(duì)象,它跟持久層(通常是關(guān)系型數(shù)據(jù)庫(kù))的數(shù)據(jù)結(jié)構(gòu)形成一一對(duì)應(yīng)的映射關(guān)系,如果持久層是關(guān)系型數(shù)據(jù)庫(kù),那么,數(shù)據(jù)表中的每個(gè)字段(或若干個(gè))就對(duì)應(yīng) PO 的一個(gè)(或若干個(gè))屬性。最形象的理解就是一個(gè) PO 就是數(shù)據(jù)庫(kù)中的一條記錄,好處是可以把一條記錄作為一個(gè)對(duì)象處理,可以方便的轉(zhuǎn)為其它對(duì)象。

3. DDD 基礎(chǔ)

學(xué)習(xí) DDD 前,有很多基礎(chǔ)概念需要掌握,這幅圖總結(jié)的很全,他把 DDD 劃分不同的層級(jí):

  • 最里層是值、屬性、唯一標(biāo)識(shí)等,這個(gè)是最基本的數(shù)據(jù)單位,但不能直接使用。
  • 然后是實(shí)體,這個(gè)把基礎(chǔ)的數(shù)據(jù)進(jìn)行封裝,可以直接使用,在代碼中就是封裝好的一個(gè)個(gè)實(shí)體對(duì)象。
  • 之后就是領(lǐng)域?qū)?,它按照業(yè)務(wù)劃分為不同的領(lǐng)域,比如訂單領(lǐng)域、商品領(lǐng)域、支付領(lǐng)域等。
  • 最后是應(yīng)用服務(wù),它對(duì)業(yè)務(wù)邏輯進(jìn)行編排,也可以理解為業(yè)務(wù)層。

3.1 領(lǐng)域和子域

在研究和解決業(yè)務(wù)問題時(shí),DDD 會(huì)按照一定的規(guī)則將業(yè)務(wù)領(lǐng)域進(jìn)行細(xì)分,當(dāng)領(lǐng)域細(xì)分到一定的程度后,DDD 會(huì)將問題范圍限定在特定的邊界內(nèi),在這個(gè)邊界內(nèi)建立領(lǐng)域模型,進(jìn)而用代碼實(shí)現(xiàn)該領(lǐng)域模型,解決相應(yīng)的業(yè)務(wù)問題。簡(jiǎn)言之,DDD 的領(lǐng)域就是這個(gè)邊界內(nèi)要解決的業(yè)務(wù)問題域。

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

領(lǐng)域的核心思想就是將問題域逐級(jí)細(xì)分,來降低業(yè)務(wù)理解和系統(tǒng)實(shí)現(xiàn)的復(fù)雜度。通過領(lǐng)域細(xì)分,逐步縮小服務(wù)需要解決的問題域,構(gòu)建合適的領(lǐng)域模型。

舉個(gè)簡(jiǎn)單的例子,對(duì)于保險(xiǎn)領(lǐng)域,我們可以把保險(xiǎn)細(xì)分為承保、收付、再保以及理賠等子域,而承保子域還可以繼續(xù)細(xì)分為投保、保全(壽險(xiǎn))、批改(財(cái)險(xiǎn))等子子域。

3.2 核心域、通用域和支撐域

子域可以根據(jù)重要程度和功能屬性劃分為如下:

  • 核心域:決定產(chǎn)品和公司核心競(jìng)爭(zhēng)力的子域,它是業(yè)務(wù)成功的主要因素和公司的核心競(jìng)爭(zhēng)力。
  • 通用域:沒有太多個(gè)性化的訴求,同時(shí)被多個(gè)子域使用的通用功能的子域。
  • 支撐域:但既不包含決定產(chǎn)品和公司核心競(jìng)爭(zhēng)力的功能,也不包含通用功能的子域。

核心域、支撐域和通用域的主要目標(biāo):通過領(lǐng)域劃分,區(qū)分不同子域在公司內(nèi)的不同功能屬性和重要性,從而公司可對(duì)不同子域采取不同的資源投入和建設(shè)策略,其關(guān)注度也會(huì)不一樣。

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

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

因此,在公司建立領(lǐng)域模型時(shí),我們就要結(jié)合公司戰(zhàn)略重點(diǎn)和商業(yè)模式,重點(diǎn)關(guān)注核心域。

3.3 通用語言和限界上下文

  • 通用語言:就是能夠簡(jiǎn)單、清晰、準(zhǔn)確描述業(yè)務(wù)涵義和規(guī)則的語言。
  • 限界上下文:用來封裝通用語言和領(lǐng)域?qū)ο?,提供上下文環(huán)境,保證在領(lǐng)域之內(nèi)的一些術(shù)語、業(yè)務(wù)相關(guān)對(duì)象等(通用語言)有一個(gè)確切的含義,沒有二義性。

3.3.1 通用語言

通用語言是團(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è)通用語言到場(chǎng)景落地,大家可能還很模糊,其實(shí)就是把領(lǐng)域?qū)ο蟆傩?、代碼模型對(duì)象等,通過代碼和文字建立映射關(guān)系,可以通過 Excel 記錄這個(gè)關(guān)系,這樣研發(fā)可以通過代碼知道這個(gè)含義,產(chǎn)品或者業(yè)務(wù)方可以通過文字知道這個(gè)含義,溝通起來就不會(huì)有歧義,說的簡(jiǎn)單一點(diǎn),其實(shí)就是統(tǒng)一產(chǎn)品和研發(fā)的話術(shù)。

直接看下面這幅圖(來源于極客時(shí)間歐創(chuàng)新的 DDD 實(shí)戰(zhàn)課):

3.3.2 限界上下文

通用語言也有它的上下文環(huán)境,為了避免同樣的概念或語義在不同的上下文環(huán)境中產(chǎn)生歧義,DDD 在戰(zhàn)略設(shè)計(jì)上提出了“限界上下文”這個(gè)概念,用來確定語義所在的領(lǐng)域邊界。

限界上下文是一個(gè)顯式的語義和語境上的邊界,領(lǐng)域模型便存在于邊界之內(nèi)。邊界內(nèi),通用語言中的所有術(shù)語和詞組都有特定的含義。把限界上下文拆解開看,限界就是領(lǐng)域的邊界,而上下文則是語義環(huán)境。

通過領(lǐng)域的限界上下文,我們就可以在統(tǒng)一的領(lǐng)域邊界內(nèi)用統(tǒng)一的語言進(jìn)行交流。

3.4 實(shí)體和值對(duì)象

3.4.1 實(shí)體

實(shí)體 = 唯一身份標(biāo)識(shí) + 可變性【狀態(tài) + 行為】

DDD 中要求實(shí)體是唯一的且可持續(xù)變化的。意思是說在實(shí)體的生命周期內(nèi),無論其如何變化,其仍舊是同一個(gè)實(shí)體。唯一性由唯一的身份標(biāo)識(shí)來決定的??勺冃砸舱从沉藢?shí)體本身的狀態(tài)和行為。

實(shí)體以 DO(領(lǐng)域?qū)ο螅┑男问酱嬖?/strong>,每個(gè)實(shí)體對(duì)象都有唯一的 ID。我們可以對(duì)一個(gè)實(shí)體對(duì)象進(jìn)行多次修改,修改后的數(shù)據(jù)和原來的數(shù)據(jù)可能會(huì)大不相同。

但是,由于它們擁有相同的 ID,它們依然是同一個(gè)實(shí)體。比如商品是商品上下文的一個(gè)實(shí)體,通過唯一的商品 ID 來標(biāo)識(shí),不管這個(gè)商品的數(shù)據(jù)如何變化,商品的 ID 一直保持不變,它始終是同一個(gè)商品。

3.4.2 值對(duì)象

值對(duì)象 = 將一個(gè)值用對(duì)象的方式進(jìn)行表述,來表達(dá)一個(gè)具體的固定不變的概念。

當(dāng)你只關(guān)心某個(gè)對(duì)象的屬性時(shí),該對(duì)象便可作為一個(gè)值對(duì)象。我們需要將值對(duì)象看成不變對(duì)象,不要給它任何身份標(biāo)識(shí),還應(yīng)該盡量避免像實(shí)體對(duì)象一樣的復(fù)雜性。

還是舉個(gè)訂單的例子,訂單是一個(gè)實(shí)體,里面包含地址,這個(gè)地址可以只通過屬性嵌入的方式形成的訂單實(shí)體對(duì)象,也可以將地址通過 json 序列化一個(gè) string 類型的數(shù)據(jù),存到 DB 的一個(gè)字段中,那么這個(gè) Json 串就是一個(gè)值對(duì)象,是不是很好理解?

下面給個(gè)簡(jiǎn)單的圖(同樣是源于極客時(shí)間歐創(chuàng)新的 DDD 實(shí)戰(zhàn)課):

3.5 聚合和聚合根

3.5.1 聚合

聚合:我們把一些關(guān)聯(lián)性極強(qiáng)、生命周期一致的實(shí)體、值對(duì)象放到一個(gè)聚合里。聚合是領(lǐng)域?qū)ο蟮娘@式分組,旨在支持領(lǐng)域模型的行為和不變性,同時(shí)充當(dāng)一致性和事務(wù)性邊界。

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

聚合在 DDD 分層架構(gòu)里屬于領(lǐng)域?qū)?,領(lǐng)域?qū)影硕鄠€(gè)聚合,共同實(shí)現(xiàn)核心業(yè)務(wù)邏輯。跨多個(gè)實(shí)體的業(yè)務(wù)邏輯通過領(lǐng)域服務(wù)來實(shí)現(xiàn),跨多個(gè)聚合的業(yè)務(wù)邏輯通過應(yīng)用服務(wù)來實(shí)現(xiàn)。

比如有的業(yè)務(wù)場(chǎng)景需要同一個(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ù)。

3.5.2 聚合根

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

  • 首先它作為實(shí)體本身,擁有實(shí)體的屬性和業(yè)務(wù)行為,實(shí)現(xiàn)自身的業(yè)務(wù)邏輯。
  • 其次它作為聚合的管理者,在聚合內(nèi)部負(fù)責(zé)協(xié)調(diào)實(shí)體和值對(duì)象按照固定的業(yè)務(wù)規(guī)則協(xié)同完成共同的業(yè)務(wù)邏輯。
  • 最后在聚合之間,它還是聚合對(duì)外的接口人,以聚合根 ID 關(guān)聯(lián)的方式接受外部任務(wù)和請(qǐng)求,在上下文內(nèi)實(shí)現(xiàn)聚合之間的業(yè)務(wù)協(xié)同。也就是說,聚合之間通過聚合根 ID 關(guān)聯(lián)引用,如果需要訪問其它聚合的實(shí)體,就要先訪問聚合根,再導(dǎo)航到聚合內(nèi)部實(shí)體,外部對(duì)象不能直接訪問聚合內(nèi)實(shí)體。

上面講的還是有些抽象,下面看一個(gè)圖就能很好理解(同樣是源于極客時(shí)間歐創(chuàng)新的 DDD 實(shí)戰(zhàn)課):

簡(jiǎn)單概括一下:

  • 通過事件風(fēng)暴(我理解就是頭腦風(fēng)暴,不過我們一般都是先通過個(gè)人理解,然后再和相關(guān)核心同學(xué)進(jìn)行溝通),得到實(shí)體和值對(duì)象;
  • 將這些實(shí)體和值對(duì)象聚合為“投保聚合”和“客戶聚合”,其中“投保單”和“客戶”是兩者的聚合根;
  • 找出與聚合根“投保單”和“客戶”關(guān)聯(lián)的所有緊密依賴的實(shí)體和值對(duì)象;
  • 在聚合內(nèi)根據(jù)聚合根、實(shí)體和值對(duì)象的依賴關(guān)系,畫出對(duì)象的引用和依賴模型。

3.6 領(lǐng)域服務(wù)和應(yīng)用服務(wù)

3.6.1 領(lǐng)域服務(wù)

當(dāng)一些邏輯不屬于某個(gè)實(shí)體時(shí),可以把這些邏輯單獨(dú)拿出來放到領(lǐng)域服務(wù)中,理想的情況是沒有領(lǐng)域服務(wù),如果領(lǐng)域服務(wù)使用不恰當(dāng),慢慢又演化回了以前邏輯都在 service 層的局面。

可以使用領(lǐng)域服務(wù)的情況:

  • 執(zhí)行一個(gè)顯著的業(yè)務(wù)操作
  • 對(duì)領(lǐng)域?qū)ο筮M(jìn)行轉(zhuǎn)換
  • 以多個(gè)領(lǐng)域?qū)ο笞鳛檩斎雲(yún)?shù)進(jìn)行計(jì)算,結(jié)果產(chǎn)生一個(gè)值對(duì)象

3.6.2 應(yīng)用服務(wù)

應(yīng)用層作為展現(xiàn)層與領(lǐng)域?qū)拥臉蛄海怯脕肀磉_(dá)用例和用戶故事的主要手段。

應(yīng)用層通過應(yīng)用服務(wù)接口來暴露系統(tǒng)的全部功能。在應(yīng)用服務(wù)的實(shí)現(xiàn)中,它負(fù)責(zé)編排和轉(zhuǎn)發(fā),它將要實(shí)現(xiàn)的功能委托給一個(gè)或多個(gè)領(lǐng)域?qū)ο髞韺?shí)現(xiàn),它本身只負(fù)責(zé)處理業(yè)務(wù)用例的執(zhí)行順序以及結(jié)果的拼裝。通過這樣一種方式,它隱藏了領(lǐng)域?qū)拥膹?fù)雜性及其內(nèi)部實(shí)現(xiàn)機(jī)制。

應(yīng)用層相對(duì)來說是較“薄”的一層,除了定義應(yīng)用服務(wù)之外,在該層我們可以進(jìn)行安全認(rèn)證,權(quán)限校驗(yàn),持久化事務(wù)控制,或者向其他系統(tǒng)發(fā)生基于事件的消息通知,另外還可以用于創(chuàng)建郵件以發(fā)送給客戶等。

3.7 領(lǐng)域事件

領(lǐng)域事件 = 事件發(fā)布 + 事件存儲(chǔ) + 事件分發(fā) + 事件處理。

領(lǐng)域事件是一個(gè)領(lǐng)域模型中極其重要的部分,用來表示領(lǐng)域中發(fā)生的事件。忽略不相關(guān)的領(lǐng)域活動(dòng),同時(shí)明確領(lǐng)域?qū)<乙櫥蛳M煌ㄖ氖虑椋蚺c其他模型對(duì)象中的狀態(tài)更改相關(guān)聯(lián)。

下面簡(jiǎn)單說明領(lǐng)域事件:

  • 事件發(fā)布:構(gòu)建一個(gè)事件,需要唯一標(biāo)識(shí),然后發(fā)布;
  • 事件存儲(chǔ):發(fā)布事件前需要存儲(chǔ),因?yàn)榻邮蘸蟮氖陆ㄒ矔?huì)存儲(chǔ),可用于重試或?qū)~等;
  • 事件分發(fā):服務(wù)內(nèi)直接發(fā)布給訂閱者,服務(wù)外需要借助消息中間件,比如 Kafka,RabbitMQ 等;
  • 事件處理:先將事件存儲(chǔ),然后再處理。

比如下訂單后,給用戶增長(zhǎng)積分與贈(zèng)送優(yōu)惠券的需求。如果使用瀑布流的方式寫代碼。一個(gè)個(gè)邏輯調(diào)用,那么不同用戶,贈(zèng)送的東西不同,邏輯就會(huì)變得又臭又長(zhǎng)。

這里的比較好的方式是,用戶下訂單成功后,發(fā)布領(lǐng)域事件,積分聚合與優(yōu)惠券聚合監(jiān)聽訂單發(fā)布的領(lǐng)域事件進(jìn)行處理。

3.8 資源庫(kù)【倉(cāng)儲(chǔ)】

倉(cāng)儲(chǔ)介于領(lǐng)域模型和數(shù)據(jù)模型之間,主要用于聚合的持久化和檢索。它隔離了領(lǐng)域模型和數(shù)據(jù)模型,以便我們關(guān)注于領(lǐng)域模型而不需要考慮如何進(jìn)行持久化。

我們將暫時(shí)不使用的領(lǐng)域?qū)ο髲膬?nèi)存中持久化存儲(chǔ)到磁盤中。當(dāng)日后需要再次使用這個(gè)領(lǐng)域?qū)ο髸r(shí),根據(jù) key 值到數(shù)據(jù)庫(kù)查找到這條記錄,然后將其恢復(fù)成領(lǐng)域?qū)ο?,?yīng)用程序就可以繼續(xù)使用它了,這就是領(lǐng)域?qū)ο蟪志没鎯?chǔ)的設(shè)計(jì)思想。

是不是感覺這塊內(nèi)容比較抽象?直接對(duì)著 Demo 學(xué)習(xí)吧,很多東西你就會(huì)豁然開朗。

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

4.1 項(xiàng)目介紹

  • 主要是圍繞用戶、角色和兩者的關(guān)系,構(gòu)建權(quán)限分配領(lǐng)域模型。
  • 采用 DDD 4 層架構(gòu),包括用戶接口層、應(yīng)用層、領(lǐng)域?qū)雍突A(chǔ)服務(wù)層。
  • 數(shù)據(jù)通過 VO、DTO、DO、PO 轉(zhuǎn)換,進(jìn)行分層隔離。
  • 采用 SpringBoot + MyBatis Plus 框架,存儲(chǔ)用 MySQL。

4.2 工程目錄

項(xiàng)目劃分為用戶接口層、應(yīng)用層、領(lǐng)域?qū)雍突A(chǔ)服務(wù)層,每一層的代碼結(jié)構(gòu)都非常清晰,包括每一層 VO、DTO、DO、PO 的數(shù)據(jù)定義,對(duì)于每一層的公共代碼,比如常量、接口等,都抽離到 ddd-common 中。

./ddd-application??//?應(yīng)用層
├──?pom.xml
└──?src
????└──?main
????????└──?java
????????????└──?com
????????????????└──?ddd
????????????????????└──?applicaiton
????????????????????????├──?converter
????????????????????????│???└──?UserApplicationConverter.java?//?類型轉(zhuǎn)換器
????????????????????????└──?impl
????????????????????????????└──?AuthrizeApplicationServiceImpl.java?//?業(yè)務(wù)邏輯
./ddd-common
├──?ddd-common?//?通用類庫(kù)
│???├──?pom.xml
│???└──?src
│???????└──?main
│???????????└──?java
│???????????????└──?com
│???????????????????└──?ddd
│???????????????????????└──?common
│???????????????????????????├──?exception?//?異常
│???????????????????????????│???├──?ServiceException.java
│???????????????????????????│???└──?ValidationException.java
│???????????????????????????├──?result?//?返回結(jié)果集
│???????????????????????????│???├──?BaseResult.javar
│???????????????????????????│???├──?Page.java
│???????????????????????????│???├──?PageResult.java
│???????????????????????????│???└──?Result.java
│???????????????????????????└──?util?//?通用工具
│???????????????????????????????├──?GsonUtil.java
│???????????????????????????????└──?ValidationUtil.java
├──?ddd-common-application?//?業(yè)務(wù)層通用模塊
│???├──?pom.xml
│???└──?src
│???????└──?main
│???????????└──?java
│???????????????└──?com
│???????????????????└──?ddd
│???????????????????????└──?applicaiton
│???????????????????????????├──?dto?//?DTO
│???????????????????????????│???├──?RoleInfoDTO.java
│???????????????????????????│???└──?UserRoleDTO.java
│???????????????????????????└──?servic?//?業(yè)務(wù)接口
│???????????????????????????????└──?AuthrizeApplicationService.java
├──?ddd-common-domain
│???├──?pom.xml
│???└──?src
│???????└──?main
│???????????└──?java
│???????????????└──?com
│???????????????????└──?ddd
│???????????????????????└──?domain
│???????????????????????????├──?event?//?領(lǐng)域事件
│???????????????????????????│???├──?BaseDomainEvent.java
│???????????????????????????│???└──?DomainEventPublisher.java
│???????????????????????????└──?service?//?領(lǐng)域接口
│???????????????????????????????└──?AuthorizeDomainService.java
└──?ddd-common-infra
????├──?pom.xml
????└──?src
????????└──?main
????????????└──?java
????????????????└──?com
????????????????????└──?ddd
????????????????????????└──?infra
????????????????????????????├──?domain?//?DO
????????????????????????????│???└──?AuthorizeDO.java
????????????????????????????├──?dto?
????????????????????????????│???├──?AddressDTO.java
????????????????????????????│???├──?RoleDTO.java
????????????????????????????│???├──?UnitDTO.java
????????????????????????????│???└──?UserRoleDTO.java
????????????????????????????└──?repository
????????????????????????????????├──?UserRepository.java?//?領(lǐng)域倉(cāng)庫(kù)
????????????????????????????????└──?mybatis
????????????????????????????????????└──?entity?//?PO
????????????????????????????????????????├──?BaseUuidEntity.java
????????????????????????????????????????├──?RolePO.java
????????????????????????????????????????├──?UserPO.java
????????????????????????????????????????└──?UserRolePO.java
./ddd-domian??//?領(lǐng)域?qū)?
├──?pom.xml
└──?src
????└──?main
????????└──?java
????????????└──?com
????????????????└──?ddd
????????????????????└──?domain
????????????????????????├──?event?//?領(lǐng)域事件
????????????????????????│???├──?DomainEventPublisherImpl.java
????????????????????????│???├──?UserCreateEvent.java
????????????????????????│???├──?UserDeleteEvent.java
????????????????????????│???└──?UserUpdateEvent.java
????????????????????????└──?impl?//?領(lǐng)域邏輯
????????????????????????????└──?AuthorizeDomainServiceImpl.java
./ddd-infra??//?基礎(chǔ)服務(wù)層
├──?pom.xml
└──?src
????└──?main
????????└──?java
????????????└──?com
????????????????└──?ddd
????????????????????└──?infra
????????????????????????├──?config
????????????????????????│???└──?InfraCoreConfig.java??//?掃描Mapper文件
????????????????????????└──?repository
????????????????????????????├──?converter
????????????????????????????│???└──?UserConverter.java?//?類型轉(zhuǎn)換器
????????????????????????????├──?impl
????????????????????????????│???└──?UserRepositoryImpl.java
????????????????????????????└──?mapper
????????????????????????????????├──?RoleMapper.java
????????????????????????????????├──?UserMapper.java
????????????????????????????????└──?UserRoleMapper.java
./ddd-interface
├──?ddd-api??//?用戶接口層
│???├──?pom.xml
│???└──?src
│???????└──?main
│???????????├──?java
│???????????│???└──?com
│???????????│???????└──?ddd
│???????????│???????????└──?api
│???????????│???????????????├──?DDDFrameworkApiApplication.java?//?啟動(dòng)入口
│???????????│???????????????├──?converter
│???????????│???????????????│???└──?AuthorizeConverter.java?//?類型轉(zhuǎn)換器
│???????????│???????????????├──?model
│???????????│???????????????│???├──?req?//?入?yún)?req
│???????????│???????????????│???│???├──?AuthorizeCreateReq.java
│???????????│???????????????│???│???└──?AuthorizeUpdateReq.java
│???????????│???????????????│???└──?vo??//?輸出?VO
│???????????│???????????????│???????└──?UserAuthorizeVO.java
│???????????│???????????????└──?web?????//?API
│???????????│???????????????????└──?AuthorizeController.java
│???????????└──?resources?//?系統(tǒng)配置
│???????????????├──?application.yml
│???????????└──?resources?//?Sql文件
│???????????????└──?init.sql
└──?ddd-task
????└──?pom.xml
./pom.xml

4.3 數(shù)據(jù)庫(kù)

包括 3 張表,分別為用戶、角色和用戶角色表,一個(gè)用戶可以擁有多個(gè)角色,一個(gè)角色可以分配給多個(gè)用戶。

create?table?t_user
(
????id???????????bigint?auto_increment?comment?'主鍵'?primary?key,
????user_name????varchar(64)????????????????????????null?comment?'用戶名',
????password?????varchar(255)???????????????????????null?comment?'密碼',
????real_name????varchar(64)????????????????????????null?comment?'真實(shí)姓名',
????phone????????bigint?????????????????????????????null?comment?'手機(jī)號(hào)',
????province?????varchar(64)????????????????????????null?comment?'用戶名',
????city?????????varchar(64)????????????????????????null?comment?'用戶名',
????county???????varchar(64)????????????????????????null?comment?'用戶名',
????unit_id??????bigint?????????????????????????????null?comment?'單位id',
????unit_name????varchar(64)????????????????????????null?comment?'單位名稱',
????gmt_create???datetime?default?CURRENT_TIMESTAMP?not?null?comment?'創(chuàng)建時(shí)間',
????gmt_modified?datetime?default?CURRENT_TIMESTAMP?not?null?on?update?CURRENT_TIMESTAMP?comment?'修改時(shí)間',
????deleted??????bigint???default?0?????????????????not?null?comment?'是否刪除,非0為已刪除'
)comment?'用戶表'?collate?=?utf8_bin;
create?table?t_role
(
????id???????????bigint?auto_increment?comment?'主鍵'?primary?key,
????name?????????varchar(256)???????????????????????not?null?comment?'名稱',
????code?????????varchar(64)????????????????????????null?comment?'角色code',
????gmt_create???datetime?default?CURRENT_TIMESTAMP?not?null?comment?'創(chuàng)建時(shí)間',
????gmt_modified?datetime?default?CURRENT_TIMESTAMP?not?null?on?update?CURRENT_TIMESTAMP?comment?'修改時(shí)間',
????deleted??????bigint???default?0?????????????????not?null?comment?'是否已刪除'
)comment?'角色表'?charset?=?utf8;
create?table?t_user_role?(
????id???????????bigint?auto_increment?comment?'主鍵id'?primary?key,
????user_id??????bigint?????????????????????????????not?null?comment?'用戶id',
????role_id??????bigint?????????????????????????????not?null?comment?'角色id',
????gmt_create???datetime?default?CURRENT_TIMESTAMP?not?null?comment?'創(chuàng)建時(shí)間',
????gmt_modified?datetime?default?CURRENT_TIMESTAMP?not?null?comment?'修改時(shí)間',
????deleted??????bigint???default?0?????????????????not?null?comment?'是否已刪除'
)comment?'用戶角色關(guān)聯(lián)表'?charset?=?utf8;

4.4 基礎(chǔ)服務(wù)層

倉(cāng)儲(chǔ)(資源庫(kù))介于領(lǐng)域模型和數(shù)據(jù)模型之間,主要用于聚合的持久化和檢索。它隔離了領(lǐng)域模型和數(shù)據(jù)模型,以便我們關(guān)注于領(lǐng)域模型而不需要考慮如何進(jìn)行持久化。

比如保存用戶,需要將用戶和角色一起保存,也就是創(chuàng)建用戶的同時(shí),需要新建用戶的角色權(quán)限,這個(gè)可以直接全部放到倉(cāng)儲(chǔ)中:

public?AuthorizeDO?save(AuthorizeDO?user)?{
????UserPO?userPo?=?userConverter.toUserPo(user);
????if(Objects.isNull(user.getUserId())){
????????userMapper.insert(userPo);
????????user.setUserId(userPo.getId());
????}?else?{
????????userMapper.updateById(userPo);
????????userRoleMapper.delete(Wrappers.<UserRolePO>lambdaQuery()
????????????????.eq(UserRolePO::getUserId,?user.getUserId()));
????}
????List<UserRolePO>?userRolePos?=?userConverter.toUserRolePo(user);
????userRolePos.forEach(userRoleMapper::insert);
????return?this.query(user.getUserId());
}

倉(cāng)儲(chǔ)對(duì)外暴露的接口如下:

//?用戶領(lǐng)域倉(cāng)儲(chǔ)
public?interface?UserRepository?{
????//?刪除
????void?delete(Long?userId);
????//?查詢
????AuthorizeDO?query(Long?userId);
????//?保存
????AuthorizeDO?save(AuthorizeDO?user);
}

基礎(chǔ)服務(wù)層不僅僅包括資源庫(kù),與第三方的調(diào)用,都需要放到該層,Demo 中沒有該示例,我們可以看一個(gè)小米內(nèi)部具體的實(shí)際項(xiàng)目,他把第三方的調(diào)用放到了 remote 目錄中:

4.5 領(lǐng)域?qū)?/h3>

4.5.1 聚合&聚合根

我們有用戶和角色兩個(gè)實(shí)體,可以將用戶、角色和兩者關(guān)系進(jìn)行聚合,然后用戶就是聚合根,聚合之后的屬性,我們稱之為“權(quán)限”。

對(duì)于地址 Address,目前是作為字段屬性存儲(chǔ)到 DB 中,如果對(duì)地址無需進(jìn)行檢索,可以把地址作為“值對(duì)象”進(jìn)行存儲(chǔ),即把地址序列化為 Json 存,存儲(chǔ)到 DB 的一個(gè)字段中。

public?class?AuthorizeDO?{
????//?用戶ID
????private?Long?userId;
????//?用戶名
????private?String?userName;
????//?真實(shí)姓名
????private?String?realName;
????//?手機(jī)號(hào)
????private?String?phone;
????//?密碼
????private?String?password;
????//?用戶單位
????private?UnitDTO?unit;
????//?用戶地址
????private?AddressDTO?address;
????//?用戶角色
????private?List&lt;RoleDTO&gt;?roles;
}

4.5.2 領(lǐng)域服務(wù)

Demo 中的領(lǐng)域服務(wù)比較薄,通過單位 ID 后去獲取單位名稱,構(gòu)建單位信息:

@Service
public?class?AuthorizeDomainServiceImpl?implements?AuthorizeDomainService?{
????@Override
????//?設(shè)置單位信息
????public?void?associatedUnit(AuthorizeDO?authorizeDO)?{
????????String?unitName?=?"武漢小米";//?TODO:?通過第三方獲取
????????authorizeDO.getUnit().setUnitName(unitName);
????}
}

我們其實(shí)可以把領(lǐng)域服務(wù)再進(jìn)一步抽象,可以抽象出領(lǐng)域能力,通過這些領(lǐng)域能力去構(gòu)建應(yīng)用層邏輯,比如賬號(hào)相關(guān)的領(lǐng)域能力可以包括授權(quán)領(lǐng)域能力、身份認(rèn)證領(lǐng)域能力等,這樣每個(gè)領(lǐng)域能力相對(duì)獨(dú)立,就不會(huì)全部揉到一個(gè)文件中,下面是實(shí)際項(xiàng)目的領(lǐng)域?qū)咏貓D:

4.5.3 領(lǐng)域事件

領(lǐng)域事件 = 事件發(fā)布 + 事件存儲(chǔ) + 事件分發(fā) + 事件處理。

這個(gè) Demo 中,對(duì)領(lǐng)域事件的處理非常簡(jiǎn)單,還是一個(gè)應(yīng)用內(nèi)部的領(lǐng)域事件,就是每次執(zhí)行一次具體的操作時(shí),把行為記錄下來。

Demo 中沒有記錄事件的庫(kù)表,事件的分發(fā)還是同步的方式,所以 Demo 中的領(lǐng)域事件還不完善,后面我會(huì)再繼續(xù)完善 Demo 中的領(lǐng)域事件,通過 Java 消息機(jī)制實(shí)現(xiàn)解耦,甚至可以借助消息隊(duì)列,實(shí)現(xiàn)異步。

/**
?*?領(lǐng)域事件基類
?*
?*?@author?louzai
?*?@since?2021/11/22
?*/
@Getter
@Setter
@NoArgsConstructor
public?abstract?class?BaseDomainEvent<T>?implements?Serializable?{
????private?static?final?long?serialVersionUID?=?1465328245048581896L;
????/**
?????*?發(fā)生時(shí)間
?????*/
????private?LocalDateTime?occurredOn;
????/**
?????*?領(lǐng)域事件數(shù)據(jù)
?????*/
????private?T?data;
????public?BaseDomainEvent(T?data)?{
????????this.data?=?data;
????????this.occurredOn?=?LocalDateTime.now();
????}
}
/**
?*?用戶新增領(lǐng)域事件
?*
?*?@author?louzai
?*?@since?2021/11/20
?*/
public?class?UserCreateEvent?extends?BaseDomainEvent<AuthorizeDO>?{
????public?UserCreateEvent(AuthorizeDO?user)?{
????????super(user);
????}
}
/**
?*?領(lǐng)域事件發(fā)布實(shí)現(xiàn)類
?*
?*?@author?louzai
?*?@since?2021/11/20
?*/
@Component
@Slf4j
public?class?DomainEventPublisherImpl?implements?DomainEventPublisher?{
????@Autowired
????private?ApplicationEventPublisher?applicationEventPublisher;
????@Override
????public?void?publishEvent(BaseDomainEvent?event)?{
????????log.debug("發(fā)布事件,event:{}",?GsonUtil.gsonToString(event));
????????applicationEventPublisher.publishEvent(event);
????}
}

4.4 應(yīng)用層

應(yīng)用層就非常好理解了,只負(fù)責(zé)簡(jiǎn)單的邏輯編排,比如創(chuàng)建用戶授權(quán):

@Transactional(rollbackFor?=?Exception.class)
public?void?createUserAuthorize(UserRoleDTO?userRoleDTO){
????//?DTO轉(zhuǎn)為DO
????AuthorizeDO?authorizeDO?=?userApplicationConverter.toAuthorizeDo(userRoleDTO);
????//?關(guān)聯(lián)單位單位信息
????authorizeDomainService.associatedUnit(authorizeDO);
????//?存儲(chǔ)用戶
????AuthorizeDO?saveAuthorizeDO?=?userRepository.save(authorizeDO);
????//?發(fā)布用戶新建的領(lǐng)域事件
????domainEventPublisher.publishEvent(new?UserCreateEvent(saveAuthorizeDO));
}

查詢用戶授權(quán)信息:

@Override
??public?UserRoleDTO?queryUserAuthorize(Long?userId)?{
??????//?查詢用戶授權(quán)領(lǐng)域數(shù)據(jù)
??????AuthorizeDO?authorizeDO?=?userRepository.query(userId);
??????if?(Objects.isNull(authorizeDO))?{
??????????throw?ValidationException.of("UserId?is?not?exist.",?null);
??????}
??????//?DO轉(zhuǎn)DTO
??????return?userApplicationConverter.toAuthorizeDTO(authorizeDO);
??}

細(xì)心的同學(xué)可以發(fā)現(xiàn),我們應(yīng)用層和領(lǐng)域?qū)?,通過 DTO 和 DO 進(jìn)行數(shù)據(jù)轉(zhuǎn)換。

4.5 用戶接口層

最后就是提供 API 接口:

@GetMapping("/query")
public?Result<UserAuthorizeVO>?query(@RequestParam("userId")?Long?userId){
????UserRoleDTO?userRoleDTO?=?authrizeApplicationService.queryUserAuthorize(userId);
????Result<UserAuthorizeVO>?result?=?new?Result<>();
????result.setData(authorizeConverter.toVO(userRoleDTO));
????result.setCode(BaseResult.CODE_SUCCESS);
????return?result;
}
@PostMapping("/save")
public?Result<Object>?create(@RequestBody?AuthorizeCreateReq?authorizeCreateReq){
????authrizeApplicationService.createUserAuthorize(authorizeConverter.toDTO(authorizeCreateReq));
????return?Result.ok(BaseResult.INSERT_SUCCESS);
}

數(shù)據(jù)的交互,包括入?yún)ⅰTO 和 VO,都需要對(duì)數(shù)據(jù)進(jìn)行轉(zhuǎn)換。

4.6 項(xiàng)目運(yùn)行

新建庫(kù)表:通過文件 "ddd-interface/ddd-api/src/main/resources/init.sql" 新建庫(kù)表。

修改 SQL 配置:修改 "ddd-interface/ddd-api/src/main/resources/application.yml" 的數(shù)據(jù)庫(kù)配置。

啟動(dòng)服務(wù):直接啟動(dòng)服務(wù)即可。

測(cè)試用例:

請(qǐng)求 URL:http://127.0.0.1:8087/api/user/save

Post body:{&quot;userName":"louzai","realName":"樓","phone":13123676844,"password":"***","unitId":2,"province":"湖北省","city":"鄂州市","county":"葛店開發(fā)區(qū)","roles":[{"roleId":2}]}

4.7 項(xiàng)目地址

DDD Demo 代碼已經(jīng)上傳到 GitHub 中:

https://github.com/lml200701158/ddd-framewor

或者通過下面命令直接獲?。?/p>

git?clone?git@github.com:lml200701158/ddd-framework.git

5. 結(jié)語

最后,談?wù)劧鐚?duì) DDD 的理解,我覺得 DDD 不像一門技術(shù),我理解的技術(shù)比如高并發(fā)、緩存、消息隊(duì)列等,DDD 更像是一項(xiàng)軟技能,一種方法論,包含了很多設(shè)計(jì)理念。

大家不要認(rèn)為,掌握了一些概念,以及 DDD 的基本思想,就掌握了 DDD,然后做項(xiàng)目時(shí),照葫蘆畫瓢,這樣你會(huì)死的很慘!

只掌握 DDD 表面的東西,其實(shí)是不夠的,我覺得 DDD 最復(fù)雜的地方,其實(shí)是在它的領(lǐng)域設(shè)計(jì)部分,項(xiàng)目啟動(dòng)前,你一定要設(shè)計(jì)各個(gè)領(lǐng)域?qū)ο?,以及它們直接的交互關(guān)系。

比如我們之前做過一個(gè)項(xiàng)目,因?yàn)檫@塊沒有做好,大家一邊寫代碼,一邊還在思考,這個(gè)領(lǐng)域?qū)ο笤撊绾螛?gòu)造,嚴(yán)重影響開發(fā)效率,最后又不得不回退到 MVC 的模式。

不要為了炫技,啥都要搞個(gè) DDD,兩者如何選擇:

  • MVC:上來就可以開干,短平快,前期用起來很香,整體開發(fā)效率也更高,所以對(duì)于緊急,或者不那么重要的項(xiàng)目,我會(huì)直接用 MVC 懟,不好的地方就是,后面會(huì)越來越復(fù)雜,可能最后就是一坨屎山,但是很多時(shí)候,比如老板進(jìn)度催的緊,我哪想到那么多以后呢?
  • DDD:前期需要花大量時(shí)間設(shè)計(jì)好領(lǐng)域模型,對(duì)于一些基礎(chǔ)組件,或者一些核心服務(wù),如果對(duì)象模型非常復(fù)雜,建議采用 DDD,前期可能會(huì)稍微痛苦一些,但是后期維護(hù)起來會(huì)非常方便。

以上就是DDD框架落地實(shí)戰(zhàn)的詳細(xì)內(nèi)容,更多關(guān)于DDD框架的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • kafka消費(fèi)者kafka-console-consumer接收不到數(shù)據(jù)的解決

    kafka消費(fèi)者kafka-console-consumer接收不到數(shù)據(jù)的解決

    這篇文章主要介紹了kafka消費(fèi)者kafka-console-consumer接收不到數(shù)據(jù)的問題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-03-03
  • java實(shí)戰(zhàn)案例之用戶注冊(cè)并發(fā)送郵件激活/發(fā)送郵件驗(yàn)證碼

    java實(shí)戰(zhàn)案例之用戶注冊(cè)并發(fā)送郵件激活/發(fā)送郵件驗(yàn)證碼

    現(xiàn)在很多的網(wǎng)站都提供有用戶注冊(cè)功能,當(dāng)我們注冊(cè)成功之后就會(huì)收到封注冊(cè)網(wǎng)站的郵件,郵件里包含了我們的注冊(cè)的用戶名和密碼及激活賬戶的超鏈接等信息,這篇文章主要給大家介紹了關(guān)于java實(shí)戰(zhàn)案例之用戶注冊(cè)并發(fā)送郵件激活/發(fā)送郵件驗(yàn)證碼的相關(guān)資料,需要的朋友可以參考下
    2021-09-09
  • 輕松掌握java中介者模式

    輕松掌握java中介者模式

    這篇文章主要幫助大家輕松掌握java中介者模式,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-09-09
  • Java中獲取鍵盤輸入值的三種方法介紹

    Java中獲取鍵盤輸入值的三種方法介紹

    這篇文章主要介紹了Java中獲取鍵盤輸入值的三種方法介紹,具有一定參考價(jià)值,需要的朋友可以了解下。
    2017-11-11
  • java對(duì)象轉(zhuǎn)換String類型的三種方法

    java對(duì)象轉(zhuǎn)換String類型的三種方法

    在很多情況下我們都需要將一個(gè)對(duì)象轉(zhuǎn)換為String類型。一般來說有三種方法可以實(shí)現(xiàn):Object.toString()、(String)Object、String.valueOf(Object)。下面對(duì)這三種方法一一分析
    2013-11-11
  • Spring Boot項(xiàng)目實(shí)戰(zhàn)之?dāng)r截器與過濾器

    Spring Boot項(xiàng)目實(shí)戰(zhàn)之?dāng)r截器與過濾器

    這篇文章主要介紹了Spring Boot項(xiàng)目實(shí)戰(zhàn)之?dāng)r截器與過濾器,文中給大家詳細(xì)介紹了springboot 攔截器和過濾器的基本概念,過濾器的配置,需要的朋友可以參考下
    2018-01-01
  • spring boot 默認(rèn)異常處理的實(shí)現(xiàn)

    spring boot 默認(rèn)異常處理的實(shí)現(xiàn)

    這篇文章主要介紹了spring boot 默認(rèn)異常處理的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-04-04
  • SparkSQL開窗函數(shù)分析使用示例

    SparkSQL開窗函數(shù)分析使用示例

    開窗函數(shù)的引入是為了既顯示聚集前的數(shù)據(jù),又顯示聚集后的數(shù)據(jù)。即在每一行的最后一列添加聚合函數(shù)的結(jié)果。開窗用于為行定義一個(gè)窗口,它對(duì)一組值進(jìn)行操作,不需要使用 GROUP BY 子句對(duì)數(shù)據(jù)進(jìn)行分組,能夠在同一行中同時(shí)返回基礎(chǔ)行的列和聚合列
    2023-01-01
  • Java Assert.assertEquals案例詳解

    Java Assert.assertEquals案例詳解

    這篇文章主要介紹了Java Assert.assertEquals案例詳解,本篇文章通過簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下
    2021-08-08
  • Java7和Java8中的ConcurrentHashMap原理解析

    Java7和Java8中的ConcurrentHashMap原理解析

    這篇文章主要介紹了Java7和Java8中的ConcurrentHashMap原理解析,對(duì)ConcurrentHashMap感興趣的讀者,一定要好好看一下
    2021-04-04

最新評(píng)論