亚洲精品中文免费|亚洲日韩中文字幕制服|久久精品亚洲免费|一本之道久久免费

      
      

            <dl id="hur0q"><div id="hur0q"></div></dl>

                迄今為止最完整的DDD實踐

                迄今為止最完整的DDD實踐

                作者:章磊(章三) 阿里飛豬技術團隊

                一、為什么需要DDD

                對于一個架構(gòu)師來說,在軟件開發(fā)中如何降低系統(tǒng)復雜度是一個永恒的挑戰(zhàn)。

                • 復雜系統(tǒng)設計: 系統(tǒng)多,業(yè)務邏輯復雜,概念不清晰,有什么合適的方法幫助我們理清楚邊界,邏輯和概念
                • 多團隊協(xié)同: 邊界不清晰,系統(tǒng)依賴復雜,語言不統(tǒng)一導致溝通和理解困難。有沒有一種方式把業(yè)務和技術概念統(tǒng)一,大家用一種語言溝通。例如:航程是大家所理解的航程嗎?
                • 設計與實現(xiàn)一致性: PRD,詳細設計和代碼實現(xiàn)天差萬別。有什么方法可以把業(yè)務需求快速轉(zhuǎn)換為設計,同時還要保持設計與代碼的一致性?
                • 架構(gòu)統(tǒng)一,可復用資產(chǎn)和擴展性: 當前取決于開發(fā)的同學具備很好的抽象能力和高編程的技能。有什么好的方法指導我們做抽象和實現(xiàn)。

                二、DDD的價值

                • 邊界清晰的設計方法: 通過領域劃分,識別哪些需求應該在哪些領域,不斷拉齊團隊對需求的認知,分而治之,控制規(guī)模。
                • 統(tǒng)一語言: 團隊在有邊界的上下文中有意識地形成對事物進行統(tǒng)一的描述,形成統(tǒng)一的概念(模型)。
                • 業(yè)務領域的知識沉淀: 通過反復論證和提煉模型,使得模型必須與業(yè)務的真實世界保持一致。促使知識(模型)可以很好地傳遞和維護。
                • 面向業(yè)務建模: 領域模型與數(shù)據(jù)模型分離,業(yè)務復雜度和技術復雜度分離。

                三、DDD架構(gòu)

                3.1 分層架構(gòu)

                • 用戶接口層: 調(diào)用應用層完成具體用戶請求。包含:controller,遠程調(diào)用服務等
                • 應用層App: 盡量簡單,不包含業(yè)務規(guī)則,而只為了下一層中的領域對象做協(xié)調(diào)任務,分配工作,重點對領域?qū)幼鼍幣磐瓿蓮碗s業(yè)務場景。包含:AppService,消息處理等
                • 領域?qū)覦omain: 負責表達業(yè)務概念和業(yè)務邏輯,領域?qū)邮窍到y(tǒng)的核心。包含:模型,值對象,域服務,事件
                • 基礎層: 對所有商城提供技術能力,包括:數(shù)據(jù)操作,發(fā)送消息,消費消息,緩存等
                • 調(diào)用關系: 用戶接口層->應用層->領域?qū)?>基礎層
                • 依賴關系:用 戶接口層->應用層->領域?qū)?>基礎層

                3.2 六邊形架構(gòu)

                • 六邊形架構(gòu): 系統(tǒng)通過適配器的方式與外部交互,將應用服務于領域服務封裝在系統(tǒng)內(nèi)部
                • 分層架構(gòu): 它依然是分層架構(gòu),它核心改變的是依賴關系。
                • 領域?qū)右蕾嚨怪茫?領域?qū)右蕾嚮A層倒置成基礎層依賴領域?qū)?,這個簡單的變化使得領域?qū)硬灰蕾嚾蝿諏樱渌麑佣家蕾囶I域?qū)?,使得領域?qū)又槐磉_業(yè)務邏輯且穩(wěn)定。

                3.3 調(diào)用鏈路

                四、DDD的基本概念

                4.1 領域模型

                領域(戰(zhàn)略):業(yè)務范圍,范圍就是邊界。子領域:領域可大可小,我們將一個領域進行拆解形成子領域,子領域還可以進行拆解。當一個領域太大的時候需要進行細化拆解。模型(戰(zhàn)術):基于某個業(yè)務領域識別出這個業(yè)務領域的聚合,聚合根,界限上下文,實體,值對象。

                4.1.1 核心域

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

                4.1.2 通用域

                沒有太多個性化的訴求,同時被多個子域使用的通用功能子域是通用域。例如,權限,登陸等等。間接對業(yè)務產(chǎn)生價值。

                4.1.3 支撐域

                支撐其他領域業(yè)務,具有企業(yè)特性,但不具有通用性。間接對業(yè)務產(chǎn)生價值。

                4.1.4 為什么要劃分核心域、通用域和支撐域

                一個業(yè)務一定有他最重要的部分,在日常做業(yè)務判斷和需求優(yōu)先級判斷的時候可以基于這個劃分來做決策。例如:一個交易相關的需求和一個配置相關的需求排優(yōu)先級,很明顯交易是核心域,規(guī)則是支持域。同樣我們認為是支撐域或者通用域的在其他公司可能是核心域,例如權限對于我們來說是通用域,但是對于專業(yè)做權限系統(tǒng)的公司,這個是核心域。

                4.2 限界上下文(戰(zhàn)略)

                業(yè)務的邊界的劃分,這個邊界可以是一個領域或者多個領域的集合。復雜業(yè)務需要多個域編排完成一個復雜業(yè)務流程。限界上下文可以作為微服務劃分的方法。其本質(zhì)還是高內(nèi)聚低耦合,只是限界上下文只是站在更高的層面來進行劃分。如何進行劃分,我的方法是一個界限上下文必須支持一個完整的業(yè)務流程,保證這個業(yè)務流程所涉及的領域都在一個限界上下文中。

                4.3 實體(ENTITY)

                定義: 實體有唯一的標識,有生命周期且具有延續(xù)性。例如一個交易訂單,從創(chuàng)建訂單我們會給他一個訂單編號并且是唯一的這就是實體唯一標識。同時訂單實體會從創(chuàng)建,支付,發(fā)貨等過程最終走到終態(tài)這就是實體的生命周期。訂單實體在這個過程中屬性發(fā)生了變化,但訂單還是那個訂單,不會因為屬性的變化而變化,這就是實體的延續(xù)性。實體的業(yè)務形態(tài): 實體能夠反映業(yè)務的真實形態(tài),實體是從用例提取出來的。領域模型中的實體是多個屬性、操作或行為的載體。實體的代碼形態(tài): 我們要保證實體代碼形態(tài)與業(yè)務形態(tài)的一致性。那么實體的代碼應該也有屬性和行為,也就是我們說的充血模型,但實際情況下我們使用的是貧血模型。貧血模型缺點是業(yè)務邏輯分散,更像數(shù)據(jù)庫模型,充血模型能夠反映業(yè)務,但過重依賴數(shù)據(jù)庫操作,而且復雜場景下需要編排領域服務,會導致事務過長,影響性能。所以我們使用充血模型,但行為里面只涉及業(yè)務邏輯的內(nèi)存操作。實體的運行形態(tài): 實體有唯一ID,當我們在流程中對實體屬性進行修改,但ID不會變,實體還是那個實體。實體的數(shù)據(jù)庫形態(tài): 實體在映射數(shù)據(jù)庫模型時,一般是一對一,也有一對多的情況。

                4.4 值對象(VALUEOBJECT)

                定義:通過對象屬性值來識別的對象,它將多個相關屬性組合為一個概念整體。在 DDD 中用來描述領域的特定方面,并且是一個沒有標識符的對象,叫作值對象。值對象沒有唯一標識,沒有生命周期,不可修改,當值對象發(fā)生改變時只能替換(例如String的實現(xiàn))值對象的業(yè)務形態(tài): 值對象是描述實體的特征,大多數(shù)情況一個實體有很多屬性,一般都是平鋪,這些數(shù)據(jù)進行分類和聚合后能夠表達一個業(yè)務含義,方便溝通而不關注細節(jié)。值對象的代碼形態(tài): 實體的單一屬性是值對象,例如:字符串,整型,枚舉。多個屬性的集合也是值對象,這個時候我們把這個集合設計為一個CLASS,但沒有ID。例如商品實體下的航段就是一個值對象。航段是描述商品的特征,航段不需要ID,可以直接整體替換。商品為什么是一個實體,而不是描述訂單特征,因為需要表達誰買了什么商品,所以我們需要知道哪一個商品,因此需要ID來標識唯一性。我們看一下下面這段代碼,person 這個實體有若干個單一屬性的值對象,比如 Id、name 等屬性;同時它也包含多個屬性的值對象,比如地址 address。

                值對象的運行形態(tài): 值對象創(chuàng)建后就不允許修改了,只能用另外一個值對象來整體替換。當我們修改地址時,從頁面?zhèn)魅胍粋€新的地址對象替換調(diào)用person對象的地址即可。如果我們把address設計成實體,必然存在ID,那么我們需要從頁面?zhèn)魅氲牡刂穼ο蟮腎D與person里面的地址對像的ID進行比較,如果相同就更新,如果不同先刪除數(shù)據(jù)庫在新增數(shù)據(jù)。值對象的數(shù)據(jù)庫形態(tài): 有兩種方式嵌入式和序列化大對象。案例1:以屬性嵌入的方式形成的人員實體對象,地址值對象直接以屬性值嵌入人員實體中。

                當我們只有一個地址的時候使用嵌入式比較好,如果多個地址必須有序列化大對象。同時可以支持搜索。

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

                支持多個地址存儲,不支持搜索。

                值對象的優(yōu)勢和局限:1.簡化數(shù)據(jù)庫設計,提升數(shù)據(jù)庫操作的性能(多表新增和修改,關聯(lián)表查詢)2.雖然簡化數(shù)據(jù)庫設計,但是領域模型還是可以表達業(yè)務3.序列化的方式會使搜索實現(xiàn)困難(通過搜索引擎可以解決)

                4.5 聚合和聚合根

                多個實體和值對象組成的我們叫聚合,聚合的內(nèi)部一定的高內(nèi)聚。這個聚合里面一定有一個實體是聚合根。聚合與領域的關系:聚合也是范圍的劃分,領域也是范圍的劃分。領域與聚合可以是一對一,也可以是一對多的關系聚合根的作用是保證內(nèi)部的實體的一致性,對外只需要對聚合根進行操作。

                4.6 限界上下文,域,聚合,實體,值對象的關系

                領域包含限界上下文,限界上下文包含子域,子域包含聚合,聚合包含實體和值對象

                4.7 事件風暴

                參與者

                除了領域?qū)<?,事件風暴的其他參與者可以是DDD專家、架構(gòu)師、產(chǎn)品經(jīng)理、項目經(jīng)理、開發(fā)人員和測試人員等項目團隊成員

                事件風暴準備的材料

                一面墻和一支筆。

                事件風暴的關注點

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

                實體執(zhí)行命令產(chǎn)生事件。

                業(yè)務場景的分析

                通過業(yè)務場景和用例找出實體,命令,事件。

                領域建模

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

                五、如何建模

                • 用例場景梳理:就是一句話需求,但我們需要把一些模糊的概念通過對話的方式逐步得到明確的需求,在加以提煉和抽象。
                • 建模方法論:詞法分析(找名詞和動詞),領域邊界
                • 模型驗證

                5.1 協(xié)同單自動化分單案例

                5.1.1 領域建模

                需求:我們需要把系統(tǒng)自動化失敗轉(zhuǎn)人工訂單自動分配給小二,避免人工挑單和搶單,通過自動分配提升整體履約處理效率。

                • 產(chǎn)品小A:把需求讀了一遍…….。
                • 開發(fā)小B:那就是將履約單分配給個小二對吧?
                • 產(chǎn)品小A:不對,我們還需要根據(jù)一個規(guī)則自動分單,例如退票訂單分給退票的小二
                • 開發(fā)小B:恩,那我們可以做一個分單規(guī)則管理。例如:新增一個退票分單規(guī)則,在里面添加一批小二工號。履約單基于自身屬性去匹配分單規(guī)則并找到一個規(guī)則,然后從分單規(guī)則里面選擇一個小二工號,履約單寫入小二工號即可。
                • 產(chǎn)品小A:分單規(guī)則還需要有優(yōu)先級,其中小二如果上班了才分配,如果下班了就不分配。
                • 開發(fā)小B:優(yōu)先級沒有問題,在匹配分單規(guī)則方法里面按照優(yōu)先級排序即可,不影響模型。而小二就不是簡單一個工號維護在分單規(guī)則中,小二有狀態(tài)了。
                • 產(chǎn)品小A:分單規(guī)則里面添加小二操作太麻煩了,例如:每次新增一個規(guī)則都要去挑人,人也不一定記得住,實際客服在管理小二的時候是按照技能組管理的。
                • 開發(fā)小B:恩,懂了,那就是通過新增一個技能組管理模塊來管理小二。然后在通過分單規(guī)則來配置1個技能組即可。獲取一個小二工號就在技能組里面了。
                • 開發(fā)小B:總感覺不對,因為新增一個自動化分單需求,履約單就依賴了分單規(guī)則,履約單應該是一個獨立的域,分單不是履約的能力,履約單實際只需要知道處理人是誰,至于怎么分配的他不太關心。應該由分單規(guī)則基于履約單屬性找匹配一個規(guī)則,然后基于這個規(guī)則找到一個小二。履約單與分單邏輯解耦。
                • 產(chǎn)品小A:分單要輪流分配或者能者多勞分配,小二之前處理過的訂單和航司優(yōu)先分配。
                • 開發(fā)小B:獲取小二的邏輯越來越復雜了,實際技能組才是找小二的核心,分單規(guī)則核心是通過履約單特征得到一個規(guī)則結(jié)果(技能組ID,分單策略,特征規(guī)則)。技能組基于分單規(guī)則的結(jié)果獲得小二工號。
                • 產(chǎn)品小A:還漏了一個信息,就是履約單會被多次分配的情況,每一個履約環(huán)節(jié)都可能轉(zhuǎn)人工,客服需要知道履約單被處理多次的情況
                • 開發(fā)小B:那用履約單無法表達了,我們需要新增一個概念叫協(xié)同單,協(xié)同單是為了協(xié)同履約單,通過協(xié)同推進履約單的進度。
                • 產(chǎn)品小A:協(xié)同單概念很好,小二下班后,如果沒有處理完,還可以轉(zhuǎn)交給別人。
                • 開發(fā)小B:恩,那只需要在協(xié)同單上增加行為即可

                5.1.2 領域劃分

                溝通的過程就是推導和驗證模型的過程,最后進行域的劃分:

                5.1.3 場景梳理

                窮舉所有場景,重新驗證模型是否可以覆蓋所有場景。

                場景名稱

                場景動作

                域服務

                事件

                聚合根

                方法

                創(chuàng)建協(xié)同單

                1、判斷關聯(lián)業(yè)務單是否非法

                協(xié)同單

                創(chuàng)建協(xié)同單

                1、問題分類是否符合條件

                (例如:商家用戶發(fā)起自營->商家的協(xié)同單)

                2、save

                協(xié)同單

                創(chuàng)建協(xié)同單

                分配協(xié)同單

                協(xié)同單ID

                分配協(xié)同單到人.

                1、判斷協(xié)同單狀態(tài)(=待處理)

                2、記錄操作日志

                3、save

                協(xié)同單

                分配協(xié)同單

                協(xié)同單

                分配協(xié)同單

                受理協(xié)同單

                協(xié)同單ID

                處理協(xié)同單

                協(xié)同單

                受理協(xié)同單

                1.判斷訂單狀態(tài)(=待處理/驗收失敗)

                2.更改訂單狀態(tài)(待處理/驗收失敗->處理中)

                3.記錄操作日志

                4.save

                協(xié)同單

                受理協(xié)同單

                轉(zhuǎn)交協(xié)同單

                協(xié)同單ID

                轉(zhuǎn)交協(xié)同單

                協(xié)同單

                轉(zhuǎn)交協(xié)同單

                1.判斷訂單狀態(tài).(=處理中、待處理)

              1. 校驗轉(zhuǎn)交的人是否在正確的組織下面
              2. 2.更改協(xié)同人值對象(同一組織下的不同人,從坐席管理域中?。?/p>

                3.記錄操作日志

                4.save

                協(xié)同單

                轉(zhuǎn)交協(xié)同單

                關閉協(xié)同單

                協(xié)同單ID

                關閉協(xié)同單

                協(xié)同單

                關閉協(xié)同單

                1.判斷訂單狀態(tài)

                (=處理中、待處理)

                2.更改訂單狀態(tài)

                (關閉)

                3.記錄操作日志

                4.save

                協(xié)同單

                關閉協(xié)同單

                處理協(xié)同單

                協(xié)同單ID

                處理協(xié)同單

                協(xié)同單

                處理協(xié)同單

                1.判斷訂單狀態(tài)

                (=處理中)

                2.更改訂單狀態(tài)(處理中->待驗收)

                3.記錄操作日志

                4.save

                協(xié)同單

                處理協(xié)同單

                駁回協(xié)同單

                協(xié)同單ID

                駁回協(xié)同單

                協(xié)同單

                駁回協(xié)同單

                1.判斷訂單狀態(tài)

                (=待驗收)

                2.更改訂單狀態(tài)(待驗收->處理中)

                3.記錄操作日志

                4.save

                協(xié)同單

                駁回協(xié)同單

                完結(jié)協(xié)同單

                協(xié)同單ID

                完結(jié)協(xié)同單

                協(xié)同單

                完結(jié)協(xié)同單

                1.判斷訂單狀態(tài)

                (=待驗收)

                2.更改訂單狀態(tài)(待驗收->已完結(jié))

                3.記錄操作日志

                4.save

                協(xié)同單

                完結(jié)協(xié)同單

                拒絕協(xié)同單

                協(xié)同單ID

                拒絕協(xié)同單

                協(xié)同單

                拒絕協(xié)同單

                1.判斷訂單狀態(tài)(=處理中、待處理)

                2.更改訂單狀態(tài)(已拒絕)

                3.記錄操作日志

                4.save

                協(xié)同單

                拒絕協(xié)同單

                催單

                協(xié)同單ID

                催單

                協(xié)同單

                催單

                1.判斷訂單狀態(tài)(=處理中、待處理)

                2、修改催單值對象

                3、記錄操作日志

                4、save

                協(xié)同單

                催單

                六、怎么寫代碼

                6.1 DDD規(guī)范

                每一層都定義了相應的接口主要目的是規(guī)范代碼:

                application:CRQS模式,ApplicationCmdService是command,ApplicationQueryService是query

                service:是領域服務規(guī)范,其中定義了DomainService,應用系統(tǒng)需要繼承它。

                model:是聚合根,實體,值對象的規(guī)范。

                • Aggregate和BaseAggregate:聚合根定義
                • Entity和BaseEntity:實體定義
                • Value和BaseValue:值對象定義
                • Param和BaseParam:領域?qū)訁?shù)定義,用作域服務,聚合根和實體的方法參數(shù)
                • Lazy:描述聚合根屬性是延遲加載屬性,類似與hibernate。
                • Field:實體屬性,用來實現(xiàn)update-tracing

                /** * 實體屬性,update-tracing * @param */public final class Field implements Changeable { private boolean changed = false; private T value; private Field(T value){ this.value = value; } public void setValue(T value){ if(!equalsValue(value)){ this.changed = true; } this.value = value; } @Override public boolean isChanged() { return changed; } public T getValue() { return value; } public boolean equalsValue(T value){ if(this.value == null && value == null){ return true; } if(this.value == null){ return false; } if(value == null){ return false; } return this.value.equals(value); } public static Field build(T value){ return new Field(value); }}

                repository

                  • Repository:倉庫定義
                  • AggregateRepository:聚合根倉庫,定義聚合根常用的存儲和查詢方法

                event:事件處理

                exception:定義了不同層用的異常

                  • AggregateException:聚合根里面拋的異常
                  • RepositoryException:基礎層拋的異常
                  • EventProcessException:事件處理拋的

                6.2 工程結(jié)構(gòu)

                6.2.1 application模塊

                • CRQS模式:commad和query分離。
                • 重點做跨域的編排工作,無業(yè)務邏輯

                6.2.2 domain模塊

                域服務,聚合根,值對象,領域參數(shù),倉庫定義

                6.2.3 infrastructurre模塊

                所有技術代碼在這一層。mybatis,redis,mq,job,opensearch代碼都在這里實現(xiàn),domain通過依賴倒置不依賴這些技術代碼和JAR。

                6.2.4 client模塊

                對外提供服務

                6.2.5 model模塊

                內(nèi)外都要用的共享對象

                6.3 代碼示例

                6.3.1 application示例

                public interface CaseAppFacade extends ApplicationCmdService { /** * 接手協(xié)同單 * @param handleCaseDto * @return */ ResultDO handle(HandleCaseDto handleCaseDto);}public class CaseAppImpl implements CaseAppFacade { @Resource private CaseService caseService;//域服務 @Resource CaseAssembler caseAssembler;//DTO轉(zhuǎn)Param @Override public ResultDO handle(HandleCaseDto handleCaseDto) { try { ResultDO resultDO = caseService.handle(caseAssembler.from(handleCaseDto)); if (resultDO.isSuccess()) { pushMsg(handleCaseDto.getId()); return ResultDO.buildSuccessResult(null); } return ResultDO.buildFailResult(resultDO.getMsg()); } catch (Exception e) { return ResultDO.buildFailResult(e.getMessage()); } }}

                • mapstruct:VO,DTO,PARAM,DO,PO轉(zhuǎn)換非常方便,代碼量大大減少。
                • CaseAppImpl.handle調(diào)用域服務caseService.handle

                6.3.2 domainService示例

                public interface CaseService extends DomainService { /** * 接手協(xié)同單 * * @param handleParam * @return */ ResultDO handle(HandleParam handleParam); }public class CaseServiceImpl implements CaseService { @Resourceprivate CoordinationRepository coordinationRepository; @Override public ResultDO handle(HandleParam handleParam) { SyncLock lock = null; try { lock = coordinationRepository.syncLock(handleParam.getId().toString()); if (null == lock) { return ResultDO.buildFailResult(“協(xié)同單handle加鎖失敗”); } CaseAggregate caseAggregate = coordinationRepository.query(handleParam.getId()); caseAggregate.handle(handleParam.getFollowerValue()); coordinationRepository.save(caseAggregate); return ResultDO.buildSuccessResult(null); } catch (RepositoryException | AggregateException e) { String msg = LOG.error4Tracer(OpLogConstant.traceId(handleParam.getId()), e, “協(xié)同單handle異常”); return ResultDO.buildFailResult(msg); } finally { if (null != lock) { coordinationRepository.unlock(lock); } } }}

                • 領域?qū)硬灰蕾嚮A層的實現(xiàn): coordinationRepository只是接口,在領域?qū)佣x好,由基礎層依賴領域?qū)訉崿F(xiàn)這個接口
                • 業(yè)務邏輯和技術解耦: 域服務這層通過調(diào)用coordinationRepository和聚合根將業(yè)務邏輯和技術解耦。
                • 聚合根的方法無副作用: 聚合根的方法只對聚合根內(nèi)部實體屬性的改變,不做持久化動作,可反復測試。
                • 模型與數(shù)據(jù)分離: 改變模型:caseAggregate.handle(handleParam.getFollowerValue()); 改變數(shù)據(jù):coordinationRepository.save(caseAggregate);事務是在save方法上

                6.3.3 Aggregate,Entity示例

                public class CaseAggregate extends BaseAggregate implements NoticeMsgBuilder { private final CaseEntity caseEntity; public CaseAggregate(CaseEntity caseEntity) { this.caseEntity = caseEntity; } /** * 接手協(xié)同單 * @param followerValue * @return */ public void handle(FollowerValue followerValue) throws AggregateException { try { this.caseEntity.handle(followerValue); } catch (Exception e) { throw e; } }}public class CaseEntity extends BaseEntity { /** * 創(chuàng)建時間 */ private Field gmtCreate; /** * 修改時間 */ private Field gmtModified; /** * 問題分類 */ private Field caseType; /** * 是否需要支付 */ private Field needPayFlag; /** * 是否需要自動驗收通過協(xié)同單 */ private Field autoAcceptCoordinationFlag; /** * 發(fā)起協(xié)同人值對象 */ private Field creatorValue; /** * 跟進人 */ private Field followerValue; /** * 狀態(tài) */ private Field status; /** * 關聯(lián)協(xié)同單id */ private Field relatedCaseId; /** * 關聯(lián)協(xié)同單類型 * @see 讀配置 com.alitrip.agent.business.flight.common.model.dataobject.CoordinationCaseTypeDO */ private Field relatedBizType; /** * 支付狀態(tài) */ private Field payStatus; 省略…. public CaseFeatureValue getCaseFeatureValue() { return get(caseFeatureValue); } public Boolean isCaseFeatureValueChanged() { return caseFeatureValue.isChanged(); } public void setCaseFeatureValue(CaseFeatureValue caseFeatureValue) { this.caseFeatureValue = set(this.caseFeatureValue, caseFeatureValue); } public Boolean isPayStatusChanged() { return payStatus.isChanged(); } public Boolean isGmtCreateChanged() { return gmtCreate.isChanged(); } public Boolean isGmtModifiedChanged() { return gmtModified.isChanged(); } public Boolean isCaseTypeChanged() { return caseType.isChanged(); } 省略…. /** * 接手 */ public void handle(FollowerValue followerValue) throws AggregateException { if (isWaitProcess()||isAppointProcess()) { this.setFollowerValue(followerValue); this.setStatus(CaseStatusEnum.PROCESSING); this.setGmtModified(new Date()); initCaseRecordValue(CaseActionNameEnum.HANDLE, null, followerValue); } else { throwStatusAggregateException(); } } 省略….}

                充血模型VS貧血模型:

                • 充血模型:表達能力強,代碼高內(nèi)聚,領域內(nèi)封閉,聚合根內(nèi)部結(jié)構(gòu)對外不可見,通過聚合根的方法訪問,適合復雜企業(yè)業(yè)務邏輯。
                • 貧血模型:業(yè)務復雜之后,邏輯散落到大量方法中。

                規(guī)范大于技巧:DDD架構(gòu)可以避免引入一些其他概念,系統(tǒng)只有域,域服務,聚合根,實體,值對象,事件來構(gòu)建系統(tǒng)。

                聚合根的reconProcess的方法的業(yè)務邏輯被reconHandler和reconRiskHandler處理,必然這些handler要訪問聚合根里面的實體的屬性,那么邏輯就會散落。修改后:

                沒有引入其他概念,都是在聚合根里面組織實體完成具體業(yè)務邏輯,去掉了handler這種技術語言。

                • 聚合根和實體定義的方法是具備單一原則,復用性原則與使用場景無關,例如:不能定義手工創(chuàng)建協(xié)調(diào)單和系統(tǒng)自動創(chuàng)建協(xié)同單,應該定義創(chuàng)建協(xié)同單。
                • Update-tracing: handle方法修改屬性后,然后調(diào)用 coordinationRepository.save(caseAggregate),我們只能全量屬性更新。Update-tracing是監(jiān)控實體的變更。 Entiy定義屬性通過Field進行包裝實現(xiàn)屬性的變更狀態(tài)記錄,結(jié)合mapstruct轉(zhuǎn)換PO實現(xiàn)Update-tracing。

                修改了mapstruct生成轉(zhuǎn)換代碼的源碼,修改后生成的代碼:

                當屬性被改變后就轉(zhuǎn)換到po中,這樣就可以實現(xiàn)修改后的字段更新。修改后的mapstruct代碼地址:[email protected]:flight-agent/mapstruct.git

                • idea的get和set方法自動生成: 由于使用field包裝,需要自定義get和set生成代碼

                6.3.4 Repository示例

                public interface CoordinationRepository extends Repository { /** * 保存/更新 * @param aggregate * @throws RepositoryException */ void save(CaseAggregate aggregate) throws RepositoryException;}@Repositorypublic class CoordinationRepositoryImpl implements CoordinationRepository {@Override public void save(CaseAggregate aggregate) throws RepositoryException { try { //聚合根轉(zhuǎn)PO,update-tracing技術 CasePO casePO = caseConverter.toCasePO(aggregate.getCase()); CasePO oldCasePO = null; if (aggregate.getCase().isAppended()) { casePOMapper.insert(casePO); aggregate.getCase().setId(casePO.getId()); } else { oldCasePO = casePOMapper.selectByPrimaryKey(casePO.getId()); casePOMapper.updateByPrimaryKeySelective(casePO); } // 發(fā)送協(xié)同單狀態(tài)改變消息 if (CaseStatusEnum.FINISH.getCode().equals(casePO.getStatus()) || CaseStatusEnum.WAIT_DISTRIBUTION.getCode().equals(casePO.getStatus()) || CaseStatusEnum.PROCESSING.getCode().equals(casePO.getStatus()) || CaseStatusEnum.APPOINT_PROCESS.getCode().equals(casePO.getStatus()) || CaseStatusEnum.WAIT_PROCESS.getCode().equals(casePO.getStatus()) || CaseStatusEnum.CLOSE.getCode().equals(casePO.getStatus()) || CaseStatusEnum.REJECT.getCode().equals(casePO.getStatus()) || CaseStatusEnum.PENDING_ACCEPTANCE.getCode().equals(casePO.getStatus())) { FollowerDto followerDto = new FollowerDto(); followerDto.setCurrentFollowerId(aggregate.getCase().getFollowerValue().getCurrentFollowerId()); followerDto.setCurrentFollowerGroupId(aggregate.getCase().getFollowerValue().getCurrentFollowerGroupId()); followerDto.setCurrentFollowerType(aggregate.getCase().getFollowerValue().getCurrentFollowerType()); followerDto.setCurrentFollowerName(aggregate.getCase().getFollowerValue().getCurrentFollowerName()); //拒絕和關閉都使用CLOSE String tag = CaseStatusEnum.codeOf(casePO.getStatus()).name(); if(CaseStatusEnum.REJECT.name().equals(tag)){ tag = CaseStatusEnum.CLOSE.name(); } statusChangeProducer.send(CaseStatusChangeEvent.build() .setId(casePO.getId()) .setFollowerDto(followerDto) .setStatus(aggregate.getCase().getStatus().getCode()) .setCaseType(aggregate.getCase().getCaseType()) .setOldStatus(null != oldCasePO ? oldCasePO.getStatus() : null) .setAppointTime(aggregate.getCase().getAppointTime()), (tag)); } // 操作日志 if (CollectionUtils.isNotEmpty(aggregate.getCase().getCaseRecordValue())) { CaseRecordValue caseRecordValue = Lists.newArrayList(aggregate.getCase().getCaseRecordValue()).get(0); caseRecordValue.setCaseId(casePO.getId()); recordPOMapper.insert(caseConverter.from(caseRecordValue)); } } catch (Exception e) { throw new RepositoryException(“”, e.getMessage(), e); } }}

                • CoordinationRepository接口定義在領域?qū)?/li>
                • CoordinationRepositoryImpl實現(xiàn)在基礎層:數(shù)據(jù)庫操作都是基于聚合根操作,保證聚合根里面的實體強一致性。

                七、最后結(jié)束語

                • 好的模型,可以沉淀組織資產(chǎn),不好的模型,逐漸成為負債
                • 功能才是表象,模型才是內(nèi)在
                • 建模過程是不斷猜想與反駁的過程
                • 演化觀點是建模過程的基本心智模式
                鄭重聲明:本文內(nèi)容及圖片均整理自互聯(lián)網(wǎng),不代表本站立場,版權歸原作者所有,如有侵權請聯(lián)系管理員(admin#wlmqw.com)刪除。
                用戶投稿
                上一篇 2022年7月11日 09:32
                下一篇 2022年7月11日 09:33

                相關推薦

                • 淘寶工廠店的東西是正品嗎?淘寶工廠店為什么便宜

                  淘工廠直營店其實就是鏈接淘寶賣家與工廠的平臺,直接對接工廠型的商家,店鋪里的東西大部分價格都會很低。那么,淘工廠直營店靠譜嗎? 淘工廠直營店是靠譜的,但售后問題可能得不到很好的保障…

                  2022年11月27日
                • 《寶可夢朱紫》獒教父屬性是什么?獒教父屬性一覽

                  寶可夢朱紫里獒教父是一只很強的寶可夢,很多玩家不清楚獒教父的屬性是什么樣的,下面就給大家?guī)韺毧蓧糁熳祥峤谈笇傩砸挥[,感興趣的小伙伴一起來看看吧,希望能幫助到大家。 獒教父屬性一覽…

                  2022年11月25日
                • 《寶可夢朱紫》太晶化強力寶可夢推薦 太晶化哪些寶可夢最強?

                  寶可夢朱紫游戲中寶可夢種類繁多,不過有的寶可夢比較強,有的稍弱一些,那么太晶化化哪些寶可夢最強呢,為了便于大家更好的體驗游戲,這里給大家?guī)砹藢毧蓧糁熳咸Щ瘡娏毧蓧敉扑],一起來…

                  2022年11月25日
                • 數(shù)字看亮點!前十月我國造船三大指標繼續(xù)全球領先

                  央視網(wǎng)消息:工業(yè)和信息化部最新數(shù)據(jù)顯示,今年1—10月,我國造船三大指標繼續(xù)保持全球領先。隨著生產(chǎn)節(jié)奏加快,船企產(chǎn)業(yè)集中度進一步提升。 2022年1—10月,全國造船完工量、新接訂…

                  2022年11月25日
                • 寶可夢朱紫四大天王屬性怎么樣 四大天王屬性數(shù)值介紹

                  寶可夢朱紫四大天王屬性如何?四大天王的屬性數(shù)值玩家們還是挺好奇的,想要了解四大天王屬性的可以看看下面小編的介紹,小編會把四大天王的屬性數(shù)值全都分享在下面,各位趕緊來小編這里多了解一…

                  2022年11月25日
                • 寶可夢朱紫皮卡丘多少級進化 皮卡丘進化條件攻略

                  寶可夢朱紫皮卡丘進化條件是什么?皮卡丘怎么進化?作為最受歡迎的電屬性寶可夢,大家都很想知道皮卡丘進化的方法,今天小編這就在下面的攻略中分享皮卡丘進化條件,各位可以趕緊來小編這里了解…

                  2022年11月25日
                • 前十個月我國造船業(yè)三大指標穩(wěn)居世界第一

                  今年1—10月,我國造船業(yè)在國際市場的份額繼續(xù)穩(wěn)居世界第一。一批高技術高附加值船舶實現(xiàn)了批量接單、批量生產(chǎn)。 近日,中國船舶上海船舶研究設計院又新接了韓國船東三艘汽車運輸船的設計追…

                  2022年11月25日
                • 客服的崗位職責怎么寫(客服工作內(nèi)容及職責)

                  各位小伙伴們大家周一好,又到了每周一給大家分享干貨內(nèi)容的時候啦~ 本期來跟大家分享一下客服工作管理流程以及客服崗位里面的每項職能崗位的核心細則,也是干貨滿滿推薦收藏~ 一.補償流程…

                  2022年11月25日
                • 商家收到貨才會退款嗎(淘寶代付款退款錢到哪里了)

                  在淘寶上有一些人下單購買商品的時候是通過代付的形式來支付的,一般情況下是家長幫助家里的小孩或者長輩進行代付,而代付訂單和普通的訂單沒有太大的區(qū)別,不過如果發(fā)生退款的話,錢是退到哪里…

                  2022年11月25日
                • 《寶可夢朱紫》四天王屬性是什么?四天王屬性陣容一覽

                  寶可夢朱紫中玩家可以挑戰(zhàn)四天王,很多玩家想知道寶可夢朱紫四天王屬性是什么,有什么陣容?下面就帶來寶可夢朱紫四天王屬性陣容一覽,感興趣的小伙伴不要錯過,希望能幫助到大家。 四天王屬性…

                  2022年11月24日

                聯(lián)系我們

                聯(lián)系郵箱:admin#wlmqw.com
                工作時間:周一至周五,10:30-18:30,節(jié)假日休息