国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

DDD技術(shù)方案落地實踐

這篇具有很好參考價值的文章主要介紹了DDD技術(shù)方案落地實踐。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

1. 引言

從接觸領(lǐng)域驅(qū)動設(shè)計的初學(xué)階段,到實現(xiàn)一個舊系統(tǒng)改造到DDD模型,再到按DDD規(guī)范落地的3個的項目。對于領(lǐng)域驅(qū)動模型設(shè)計研發(fā),從開始的各種疑惑到吸收各種先進(jìn)的理念,目前在技術(shù)實施這一塊已經(jīng)基本比較成熟。在既往經(jīng)驗中總結(jié)了一些在開發(fā)中遇到的技術(shù)問題和解決方案進(jìn)行分享。

因為DDD的建模理論及方法論有比較成熟的教程,如《領(lǐng)域驅(qū)動設(shè)計》,這里我對DDD的理論部分只做簡要回顧,如果需要了解DDD建模和基礎(chǔ)的理論知識,請移步相關(guān)書籍進(jìn)行學(xué)習(xí)。本文主要針對我們團(tuán)隊在DDD落地實踐中的一些技術(shù)點進(jìn)行分享。

2. 理論回顧

理論部分只做部分提要,關(guān)于DDD建模及基礎(chǔ)知識相關(guān),可參考 Eric Evans 的《領(lǐng)域驅(qū)動設(shè)計》一書及其它理論書籍,這里只做部分內(nèi)容摘抄。

2.1.1 名詞

領(lǐng)域及劃分:領(lǐng)域、子域、核心域、通用域、支撐域,限界上下文;

模型:聚合、聚合根、實體、值對象;

實體

是指描述了領(lǐng)域中唯一的且可持續(xù)變化的抽象模型,有ID標(biāo)識,有生命周期,有狀態(tài)(用值對象來描述狀態(tài)),實體通過ID進(jìn)行區(qū)分;

每個實體對象都有唯一的 ID。我們可以對一個實體對象進(jìn)行多次修改,修改后的數(shù)據(jù)和原來的數(shù)據(jù)可能會大不相同。比如商品是商品上下文的一個實體,通過唯一的商品 ID 來標(biāo)識,不管這個商品的數(shù)據(jù)如何變化,商品的 ID 一直保持不變,它始終是同一個商品。

在 DDD 里,這些實體類通常采用充血模型,與這個實體相關(guān)的所有業(yè)務(wù)邏輯都在實體類的方法中實現(xiàn)。

聚合根

聚合根是實體,是一個根實體,聚合根的ID全局唯一標(biāo)識,聚合根下面的實體的ID在聚合根內(nèi)唯一即可;

聚合根是聚合還原和保存的唯一入口,聚合的還原應(yīng)該保證完整性即整存整??;

聚合設(shè)計的原則

  1. 聚合是用來封裝真正的不變性,而不是簡單的將對象組合在一起;

  2. 聚合應(yīng)盡量設(shè)計的小,主要因為業(yè)務(wù)決定聚合,業(yè)務(wù)改變聚合,盡可能小的拆分,可以避免重構(gòu),重新拆分

  3. 聚合之間的關(guān)聯(lián)通過ID,而不是對象引用;

  4. 聚合內(nèi)強(qiáng)一致性,聚合之間最終一致性;

值對象

值對象的核心本質(zhì)是值,與是否有復(fù)雜類型無關(guān),值對象沒有生命周期,通過兩個值對象的值是否相同區(qū)分是否是同一個值對象;

值對象應(yīng)該設(shè)計為只讀模式, 如果任一屬性發(fā)生變化,應(yīng)該重新構(gòu)建一個新的值對象而不是改變原來值對象的屬性;

領(lǐng)域事件

在事件風(fēng)暴過程中,會識別出命令、業(yè)務(wù)操作、實體等,此外還有事件。比如當(dāng)業(yè)務(wù)人員的描述中出現(xiàn)類似“當(dāng)完成…后,則…”,“當(dāng)發(fā)生…時,則…”等模式時,往往可將其用領(lǐng)域事件來實現(xiàn)。領(lǐng)域事件表示在領(lǐng)域中發(fā)生的事件,它會導(dǎo)致進(jìn)一步的業(yè)務(wù)操作。如電商中,支付完成后觸發(fā)的事件,會導(dǎo)致生成訂單、扣減庫存等操作。

在一次事務(wù)中,最多只能更改一個聚合的狀態(tài)。如何一個業(yè)務(wù)操作涉及多個聚合狀態(tài)的更改,可以采用領(lǐng)域事件的方式,實現(xiàn)聚合之間的解耦;在聚合根和跨上下文之間實現(xiàn)最終一致性。聚合內(nèi)數(shù)據(jù)強(qiáng)一致性,聚合之間數(shù)據(jù)最終一致性。

事件的生成和發(fā)布:構(gòu)建的事件應(yīng)包含事件ID、時間戳、事件類型、事件源等基本屬性,以便事件可以無歧義地在不同上下文間傳播;此外事件還應(yīng)包含具體的業(yè)務(wù)數(shù)據(jù)。

領(lǐng)域事件為已發(fā)生的事務(wù),具有只讀,不可變更性。一般接收消息為異步監(jiān)聽,處理的后續(xù)處理需要考慮時序和重復(fù)發(fā)送的問題。

2.1.2?聚合根、實體、值對象的區(qū)別?

從標(biāo)識的角度:

聚合根具有全局的唯一標(biāo)識,而實體只有在聚合內(nèi)部有唯一的本地標(biāo)識,值對象沒有唯一標(biāo)識;

從是否只讀的角度:

聚合根除了唯一標(biāo)識外,其他所有狀態(tài)信息都理論上可變;實體是可變的;值對象是只讀的;

從生命周期的角度:

聚合根有獨立的生命周期,實體的生命周期從屬于其所屬的聚合,實體完全由其所屬的聚合根負(fù)責(zé)管理維護(hù);值對象無生命周期可言,因為只是一個值;

2.2 建模方法

2.2.1 事件風(fēng)暴

事件?暴法類似頭腦?暴,簡單來說就是誰在何時基于什么做了什么,產(chǎn)?了什么,影響了什么事情。

在事件風(fēng)暴的過程中,領(lǐng)域?qū)<視驮O(shè)計、開發(fā)人員一起建立領(lǐng)域模型,在領(lǐng)域建模的過程中會形成通用的業(yè)務(wù)術(shù)語和用戶故事。事件風(fēng)暴也是一個項目團(tuán)隊統(tǒng)一語言的過程。

2.2.2 用戶故事

用戶故事在軟件開發(fā)過程中被作為描述需求的一種表達(dá)形式,并著重描述角色(誰要用這個功能)、功能(需要完成什么樣子的功能)和價值(為什么需要這個功能,這個功能帶來什么樣的價值)。

例:

作為一個“網(wǎng)站管理員”,我想要“統(tǒng)計每天有多少人訪問了我的網(wǎng)站”,以便于“我的贊助商了解我的網(wǎng)站會給他們帶來什么收益。

通過用戶故事分析會形成一個個的領(lǐng)域?qū)ο?,這些領(lǐng)域?qū)ο髮?yīng)領(lǐng)域模型的業(yè)務(wù)對象,每一個業(yè)務(wù)對象和領(lǐng)域?qū)ο蠖加型ㄓ玫拿~術(shù)語,并且一一映射。

2.2.3 統(tǒng)一語言

在事件風(fēng)暴和用戶故事梳理過程及日常討論中,會有越來越多的名詞冒出來,這個時候,需要團(tuán)隊成員統(tǒng)一意見,形成名詞字典。在后續(xù)的討論和描述中,使用統(tǒng)一的名稱名詞來指代模型中的對象、屬性、狀態(tài)、事件、用例等信息。

可以用Excel或者在線文檔等方式記錄存儲,標(biāo)注名稱,描述和提取時間和參與人等信息。

代碼模型設(shè)計的時侯就要建立領(lǐng)域?qū)ο蠛痛a對象的一一映射,從而保證業(yè)務(wù)模型和代碼模型的一致,實現(xiàn)業(yè)務(wù)語言與代碼語言的統(tǒng)一。

2.2.4 領(lǐng)域劃分及建模

DDD 內(nèi)核的代碼模型來源于領(lǐng)域模型,每個代碼模型的代碼對象跟領(lǐng)域?qū)ο笠灰粚?yīng)。

通過UML類圖(通過顏色標(biāo)注區(qū)分聚合根、實體、值對象等)、用例圖、時序圖完成軟件模型設(shè)計。

2.3 整潔架構(gòu)(洋蔥架構(gòu))

整潔架構(gòu)(Clean Architecture)是由Bob大叔在2012年提出的一個架構(gòu)模型,顧名思義,是為了使架構(gòu)更簡潔。

整潔架構(gòu)最主要原則是依賴原則,它定義了各層的依賴關(guān)系,越往里,依賴越低,代碼級別越高。外圓代碼依賴只能指向內(nèi)圓,內(nèi)圓不知道外圓的任何事情。一般來說,外圓的聲明(包括方法、類、變量)不能被內(nèi)圓引用。同樣的,外圓使用的數(shù)據(jù)格式也不能被內(nèi)圓使用。

整潔架構(gòu)各層主要職能如下:

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

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

  • Interface Adapters:它把適用于 Use Cases 和 entities 的數(shù)據(jù)轉(zhuǎn)換為適用于外部服務(wù)的格式,或把外部的數(shù)據(jù)格式轉(zhuǎn)換為適用于 Use Casess 和 entities 的格式。

  • Frameworks and Drivers:這是實現(xiàn)所有前端業(yè)務(wù)細(xì)節(jié)的地方,UI,Tools,F(xiàn)rameworks 等以及數(shù)據(jù)庫等基礎(chǔ)設(shè)施。

3. 落地實踐

3.1 概述

在整個DDD開發(fā)過程中,除了建模方法和理論的學(xué)習(xí),實際技術(shù)落地還會遇到很多問題。在多個項目的不斷開發(fā)演進(jìn)過程中,循序漸進(jìn)的總結(jié)了很多經(jīng)驗和小技巧,用于解決過往的缺憾和不足。走向DDD的路有千萬條,這些只是其中的一些可選方案,如有紕漏還請指正。

3.2 工程示例簡介

目前我們采用的是內(nèi)核整體分離,如下圖所示。

b2b-baseproject-kernel 內(nèi)核模塊說明

其中: b2b-baseproject-kernel 為內(nèi)核的Maven工程示例, b2b-baseproject-center為讀寫服務(wù)匯總的中心對外服務(wù)工程示例。

圖3-1 kernel基礎(chǔ)工程示例

內(nèi)核Maven工程模塊說明:

1. b2b-baseproject-kernel-common 常用工具類,常量等,不對外SDK暴露;
2. b2b-baseproject-kernel-export 內(nèi)核對外暴露的信息,為常量,枚舉等,可直接讓外部SDK依賴并對外,減少通用知識重復(fù)定義(可選);
3. b2b-baseproject-kernel-dto 數(shù)據(jù)傳輸層,方便app層和domain層共享數(shù)據(jù)傳輸對象,不對外SDK暴露;
4. b2b-baseproject-kernel-ext-sdk 擴(kuò)展點;(可選,不需要可直接移除)
5. b2b-baseproject-kernel-domain 領(lǐng)域?qū)拥龋ㄒ部梢圆粍澐肿幽K,按需劃分即可);
   (b2b-baseproject-kernel-domain-common 通用領(lǐng)域,主要為一些通用值對象;
   (b2b-baseproject-kernel-domain-ctxmain 核心領(lǐng)域模型,可自行調(diào)整名稱;
6. b2b-baseproject-kernel-read-app 讀服務(wù)應(yīng)用層;(可選,不需要可直接移除)
7. b2b-baseproject-kernel-app 寫服務(wù)應(yīng)用層;


b2b-baseproject-center 實現(xiàn)模塊說明

圖3-2 center基礎(chǔ)工程示例

center Maven工程模塊說明:

對外SDK
1. b2b-baseproject-sdk 對外sdk工程;
    1.1 b2b-baseproject-base-sdk 基礎(chǔ)sdk;
    1.2 b2b-baseproject-core-sdk 寫服務(wù)sdk;
    1.3 b2b-baseproject-svr-sdk 讀服務(wù)sdk;
基礎(chǔ)設(shè)施
2. b2b-baseproject-center-common 常用工具類,常量等;
3. b2b-baseproject-center-infrastructure 基礎(chǔ)設(shè)施實現(xiàn)層;
   (b2b-baseproject-center-dao 基礎(chǔ)設(shè)施層的數(shù)據(jù)庫訪問層,也可不分,直接融合到infrastructure);
   (b2b-baseproject-center-es  基礎(chǔ)設(shè)施層的ES訪問層,也可不分,直接融合到infrastructure);

center服務(wù)層
4. b2b-baseproject-center-service center的業(yè)務(wù)服務(wù)層;

接入層
5. b2b-baseproject-center-provider 服務(wù)接入實現(xiàn);

springboot啟動
6. b2b-baseproject-center-bootstrap springboot應(yīng)用啟動層;


備注:對外SDK主要考慮適配CQRS原則,將讀寫分為兩個單獨的module, 如果感覺麻煩,也可以合并為一個SDK對外,用不同的分包隔離即可。


內(nèi)核和實現(xiàn)的關(guān)聯(lián)

使用內(nèi)核和具體實現(xiàn)應(yīng)用分離的劃分是因為前期因為有商業(yè)化衍生出了多版本開發(fā)。當(dāng)然目前架構(gòu)組是不建議一個內(nèi)核多套實現(xiàn)的,而是建議一個內(nèi)核加上一個主版本實現(xiàn)。避免因為多版本實現(xiàn)造成分裂,徒增開發(fā)和維護(hù)成本,改為采用配置和擴(kuò)展點來滿足差異化需求。

目前我們開發(fā)只保持一個主版本,但是工程繼續(xù)使用內(nèi)核分離的方式,即一個內(nèi)核+一個主版本實現(xiàn)。

優(yōu)點:

  1. 內(nèi)核和實現(xiàn)代碼完全隔離,得到一個比較干凈存粹的內(nèi)核;

  2. 雖萬不得已不建議多版本實現(xiàn),但是萬一要支持多版本,可以直接復(fù)用內(nèi)核;

  3. 某種意義上,是一種更合理的分離,保證了內(nèi)核和實現(xiàn)版本的分離,各自關(guān)注各自模塊的核心問題;

缺點:

  1. 聯(lián)調(diào)成本增加,每次改完需要本地install 或者推送到遠(yuǎn)程Maven倉庫;

基于以上原因,對于小工程不必做以上分離,直接在一個Maven工程中進(jìn)行依賴開發(fā)即可 ,從很多示例教程也是推薦如此。

CQRS(命令與查詢職責(zé)分離)

CQRS 就是讀寫分離,讀寫分離的主要目的是為了提高查詢性能,同時達(dá)到讀、寫解耦。而 DDD 和 CQRS 結(jié)合,可以分別對讀和寫建模。

查詢模型是一種非標(biāo)準(zhǔn)化數(shù)據(jù)模型,它不反映領(lǐng)域行為,只用于數(shù)據(jù)查詢和顯示;命令模型執(zhí)行領(lǐng)域行為,在領(lǐng)域行為執(zhí)行完成后通知查詢模型。

命令模型如何通知到查詢模型呢?如果查詢模型和領(lǐng)域模型共享數(shù)據(jù)源,則可以省卻這一步;如果沒有共享數(shù)據(jù)源,可以借助于發(fā)布訂閱的消息模式通知到查詢模型,從而達(dá)到數(shù)據(jù)最終一致性。

Martin 在 blog 中指出:CQRS 適用于極少數(shù)復(fù)雜的業(yè)務(wù)領(lǐng)域,如果不是很適合反而會增加復(fù)雜度;另一個適用場景是為了獲取高性能的查詢服務(wù)。

對于寫少讀多的共享類通用數(shù)據(jù)服務(wù)(如主數(shù)據(jù)類應(yīng)用)可以采用讀寫分離架構(gòu)模式。單數(shù)據(jù)中心寫入數(shù)據(jù),通過發(fā)布訂閱模式將數(shù)據(jù)副本分發(fā)到多數(shù)據(jù)中心。通過查詢模型微服務(wù),實現(xiàn)多數(shù)據(jù)中心數(shù)據(jù)共享和查詢。

領(lǐng)域與讀模型的聯(lián)系與差異

領(lǐng)域模型(以聚合根為唯一入口)是承載本體變更的核心,其是對業(yè)務(wù)模型的根本建模。若聚合根為每一個普通的人體,聚合根主鍵就是身份證ID。假設(shè)人人生而自由,不受人控制,那么當(dāng)一個人接受到合理命令后進(jìn)行自我屬性變更,然后對外發(fā)送信息。

而視圖層是人體和社會信息的投影,就如我們的教育情況,職業(yè)情況,健康情況等一樣。是對某個時刻對本體信息的投影。

視圖因為基于消息傳播的特性,我們的很多視圖可能是延遲或者不一致的。事例:

1. 你已經(jīng)陽了,而你的健康碼還是綠碼;
2. 你已經(jīng)結(jié)婚,而戶口本還是未婚;
3. 你的結(jié)婚證上聚合了你配偶的信息;


現(xiàn)實世界的不一致已經(jīng)給我們帶來了很多麻煩和困擾,對于IT系統(tǒng)來說也是一樣。視圖的實時更新總是令人神往,但是在分布式系統(tǒng)中面臨諸多挑戰(zhàn)。而為了消除領(lǐng)域模型變更后各種視圖層的延遲和不一致,就需要在消息傳播和更新時機(jī)上做一些優(yōu)化。但是在業(yè)務(wù)處理上,還是需要容忍一定程度的延遲和不一致,因為分布式系統(tǒng)是很難做到100%的準(zhǔn)實時和一致性的。

3.3 問題及解決方案

3.3.1 領(lǐng)域資源注冊中心

背景

一般來講,領(lǐng)域模型不持有倉庫也不不持有其他服務(wù),是一個比較。這就造成領(lǐng)域模型在做一些驗證的時候,僅能進(jìn)行內(nèi)存態(tài)的驗證。對于rpc服務(wù),以及涉及一些重復(fù)性驗證的情況,就顯得無能為力。為了更好的解決這個問題,我們采用了領(lǐng)域模型注冊中心,采用一個單例的類來持有這些服務(wù);

那我們在領(lǐng)域模型中,從數(shù)據(jù)庫重新加載回來的領(lǐng)域模型,不需要通過spring進(jìn)行數(shù)據(jù)封裝,就可以直接使用所依賴的服務(wù)。

基于此,這些服務(wù)必須是無狀態(tài)的,通過輸入領(lǐng)域模型完成數(shù)據(jù)服務(wù)。

/**
 * 租戶注冊中心
 *
 * @author david
 * @date 12/12/22
 */
@NoArgsConstructor(access = AccessLevel.PRIVATE)
@Getter
@Setter
public class TenantRegistry {
    /**
     * 倉庫
     */
    private TenantRepository tenantRepository;

    /**
     * 單例
     */
    private static TenantRegistry INSTANCE = new TenantRegistry();

    /**
     * 獲取單例
     *
     * @return
     */
    public static TenantRegistry getInstance() {
        return INSTANCE;
    }

}



在領(lǐng)域模型進(jìn)行數(shù)據(jù)保存的時候,可用獲取倉庫或者驗證服務(wù)進(jìn)行數(shù)據(jù)驗證。


 /**
     * 保存數(shù)據(jù)
     */
    public void save() {
        this.validate();
        TenantRepository tenantRepository = TenantRegistry.getInstance().getTenantRepository();
        tenantRepository.save(this);
    }


3.3.2 內(nèi)核模塊化

一般來講,主站因為服務(wù)的客戶量廣,需求多樣,導(dǎo)致功能及依賴服務(wù)也會很龐大。然后在進(jìn)行商業(yè)化部署的時候,往往只需要其中10%~50%的能力,如果在部署的時候,全量的服務(wù)和領(lǐng)域模型加載意味著需要配置相關(guān)的底層資源和依賴,否則可能啟動異常。

內(nèi)核能力模塊化就顯得尤為重要,目前我們主要利用spring的條件加載實現(xiàn)內(nèi)核模塊化。如下:

/**
 * 租戶構(gòu)建工廠
 *
 * @author david
 */
@Component
@ConditionalOnExpression("${b2b.baseproject.kernel.ability.tenant:true}")
public class TenantInfoFactory {
}

/**
 * 租戶應(yīng)用服務(wù)實現(xiàn)
 *
 * @author david
 */
@Service
@ConditionalOnExpression("${b2b.baseproject.kernel.ability.tenant:true}")
public class TenantAppServiceImpl implements TenantAppService {
}

//其它相關(guān)資源類似,通過@ConditionalOnExpression("${b2b.baseproject.kernel.ability.tenant:true}") 進(jìn)行動態(tài)開關(guān);


這樣在applicaiton.yml 配置相關(guān)能力的true/false, 就可以實現(xiàn)相關(guān)能力的按需加載,當(dāng)然這是強(qiáng)依賴spring的基礎(chǔ)能力情況下。

//appliciaton.yml 配置

b2b:
  baseproject:
    kernel:
      ability:
        tenant: true
        dict: true
        scene: true


可選進(jìn)一步優(yōu)化依賴

條件加載使用了spring的注解,某種意義上導(dǎo)致內(nèi)核和spring進(jìn)行了耦合。然而,項目中總有終極DDD患者,希望內(nèi)核中最好連spring的依賴也去掉。這個時候,可以將spring的裝配專門抽取到一個Maven的module作為starter,由這個starter負(fù)責(zé)spring的相關(guān)的注入和依賴進(jìn)行適配。對于模塊化加載配置,可以繼續(xù)沿用conditional的配置,本質(zhì)上差異不大。

3.3.3 倉庫層diff實踐(可選項)

本案例僅在使用關(guān)系型數(shù)據(jù)庫,且為了提升更新時性能場景適用。如果能偏向于采用支持事務(wù)的NoSQL數(shù)據(jù)庫,那么本實踐可直接略過。

如果不是受制于關(guān)系型數(shù)據(jù)庫的更加流行的制約,在面向DDD開發(fā)之后,大家可能更偏向于NoSQL數(shù)據(jù)庫,可以將領(lǐng)域?qū)ο笠跃酆细臑檎w進(jìn)行整存整取,這樣可以大大的降低倉庫層存取持久化數(shù)據(jù)的開發(fā)量。而現(xiàn)狀是大部分項目都依賴于關(guān)系型數(shù)據(jù)庫,故而很多數(shù)據(jù)依然存在復(fù)雜的數(shù)據(jù)庫存儲關(guān)系。

如果聚合根下關(guān)聯(lián)多個實體,那么在更新的時候,比較簡潔的方式是整體覆蓋,即使數(shù)據(jù)行沒有發(fā)生變更。有時候為了提升數(shù)據(jù)庫更新的性能,就需要按需更新,這時候就需要追蹤實體對象是否發(fā)生變更。

對實體對象的變更追蹤有兩個方式:

A -> 保存更新前快照,使用反射工具深度對比值是否變更;
B -> 使用RecordLog 作為數(shù)據(jù)狀態(tài)跟蹤;


在過往項目中,A/B方案均采用過,A方案的代碼侵入較少,但是需要保留更新前完整快照,使用反射情況下性能會略有影響。 B方案不需要保持更新前完整快照, 也不用反射,但是需要在需要diff的實體對象中增加RecordLog值對象標(biāo)記數(shù)據(jù)是新增、修改、或者未變更。

目前我們主要采用B方案,在涉及實體變更的入口方法,順便調(diào)用RecordLog的更新方法,這樣在倉庫層既可以判斷是新增、修改、還是沒有發(fā)生變更。倉庫層在執(zhí)行保存的時候,則可用通過recordLog值對象的creating, updating判斷數(shù)據(jù)的狀態(tài)。

/**
 * 日志值對象,用于記錄數(shù)據(jù)日志信息
 *
 * @author david
 * @date 2020-08-24
 */
@Getter
@Setter
@ToString
@ValueObject
public class RecordLog implements Serializable, RecordLogCompatible {

    /**
     * 創(chuàng)建人
     */
    private String creator;
    /**
     * 操作人
     */
    private String operator;
    /**
     * 并發(fā)版本號,不一定以第三方傳入的為準(zhǔn)
     */
    private Integer concurrentVersion;
    /**
     * 創(chuàng)建時間,不一定以第三方傳入的為準(zhǔn)
     */
    private Date created;
    /**
     * 修改時間, 不一定以第三方傳入的為準(zhǔn)
     */
    private Date modified;

    /**
     * 創(chuàng)建中
     */
    private transient boolean creating;
    /**
     * 修改中
     */
    private transient boolean updating;

    /**
     * 創(chuàng)建時構(gòu)建
     *
     * @param creator
     * @return
     */
    public static RecordLog buildWhenCreating(String creator) {
        return buildWhenCreating(creator, new Date());
    }

    /**
     * 創(chuàng)建時構(gòu)建,傳入創(chuàng)建時間
     *
     * @param creator
     * @param createTime
     * @return
     */
    public static RecordLog buildWhenCreating(String creator, Date createTime) {
        RecordLog recordLog = new RecordLog();
        recordLog.creator = creator;
        recordLog.created = createTime;
        recordLog.modified = createTime;
        recordLog.operator = creator;
        recordLog.concurrentVersion = 1;
        recordLog.creating = true;
        return recordLog;
    }

    /**
     * 更新
     *
     * @param operator
     */
    public void update(String operator) {
        setOperator(operator);
        setModified(new Date());
        setUpdating(true);
        concurrentVersion++;
    }

}



// 實體變更的時候,需要同步標(biāo)記recordLog

public class TenantInfo implements AggregateRoot<TenantIdentifier> {

 /**
     * 失效數(shù)據(jù)
     *
     * @param operator
     */
    public void invalid(String operator) {
        setStatus(StatusEnum.NO);
        recordLog.update(operator);

    }

    /**
     * 發(fā)布
     *
     * @param operator
     */
    public void publish(String operator) {
        setBusinessStatus(TenantBusinessStatusEnum.PUBLISH);
        recordLog.update(operator);
    }
    


      /**
     * 保存到倉庫
     *
     * @param tenantInfo
     */
    @Override
    @Transactional
    public void save(TenantInfo tenantInfo) {
        TenantInfoPO tenantInfoPO = TenantInfoAssembler.convertToPO(tenantInfo);
        RecordLog recordLog = tenantInfo.getRecordLog();
        //創(chuàng)建diff判斷
        if (recordLog.isCreating()) {
            tenantInfoMapper.insert(tenantInfoPO);
        } else if (recordLog.isUpdating()) {            //更新diff判斷
            UpdateWrapper<TenantInfoPO> updateWrapper = new UpdateWrapper<>();
            updateWrapper.lambda().eq(TenantInfoPO::getTenantId, tenantInfoPO.getTenantId());
            tenantInfoMapper.update(tenantInfoPO, updateWrapper);
        }
        //將領(lǐng)域事件轉(zhuǎn)換為taskPo, 并在一個事務(wù)之中保存到數(shù)據(jù)庫,以便保證最終被消費
        tenantInfo.publish(localTaskEventFactory.buildEventPersistenceAdapter(event -> TaskAssembler.tenantEventToTaskPO(event)));
    }


3.3.4 讀服務(wù)設(shè)計

一個完整的領(lǐng)域服務(wù),只是寫入沒有讀取是不夠的,只寫不讀會出現(xiàn)信息黑洞,導(dǎo)致領(lǐng)域變更無法被外部感知和使用。如前面所述,讀服務(wù)是面向視圖的,其需要的是容易檢索(索引服務(wù)),寬表(冗余關(guān)聯(lián)信息),摘要信息。且讀服務(wù)不對源數(shù)據(jù)進(jìn)行修改,無需進(jìn)行加鎖更注重響應(yīng)快速。

目前內(nèi)核能相對標(biāo)準(zhǔn)化的讀服務(wù),主要針對聚合根進(jìn)行基本的詳情檢索,如通過聚合根主鍵返回基本視圖信息、列表檢索等;其他個性化定制化的查詢參數(shù)和響應(yīng)結(jié)果可以依據(jù)需求自行設(shè)計和擴(kuò)展,如果是比較定制的查詢服務(wù),可以不必落地到內(nèi)核之中。

在b2b-baseproject-kernel工程的 read-app 模塊中,我們定義了讀服務(wù)的接口和約束返回對象,則在實現(xiàn)的center工程中,主要實現(xiàn)底層的讀倉庫和SDK接入層即可(可通過ES, 關(guān)系型數(shù)據(jù)庫, redis 等來提供底層的檢索服務(wù))。

讀服務(wù)接口:

/**
 * 租戶應(yīng)用查詢服務(wù)
 *
 * @author david
 **/
public interface TenantInfoQueryService {

    /**
     * 通過租戶code查詢
     *
     * @param req
     * @return
     */
    TenantConstraint getTenantByCode(GetTenantByCodeReq req);
}

/**
 * 通過租戶編碼查詢租戶信息請求
 *
 * @author david
 */
@Setter
@Getter
@ToString
public class GetTenantByCodeReq implements Serializable, Verifiable {
    /**
     * 租戶編碼
     */
    private String tenantCode;

    @Override
    public void validate() {
        Validate.notEmpty(tenantCode, CodeDetailEnum.TENANT);
    }
}





/**
 * 示例租戶讀服務(wù)約束接口
 *
 * @author david
 * @date 4/15/22
 */
public interface TenantConstraint extends RecordLogCompatible {
    /**
     * 租戶id
     */
    Long getTenantId();

    /**
     * 租戶id,編碼
     */
    Integer getTenantCode();
  
    // ...
}



/**
 * 租戶應(yīng)用查詢服務(wù)內(nèi)核實現(xiàn)
 *
 * @author david
 **/
@Service
public class TenantInfoQueryServiceImpl implements TenantInfoQueryService {

    //租戶讀倉庫
    @Resource
    private TenantReadRepo tenantReadRepo;

    /**
     * 通過租戶id查詢
     *
     * @param req
     * @return
     */
    @Override
    public TenantConstraint getTenantByCode(GetTenantByCodeReq req) {
        req.validate();
        return tenantReadRepo.getTenantByCode(req.getTenantCode());
    }
  
  //...
}


3.3.5 領(lǐng)域事件發(fā)布

如果不依賴binlog和事務(wù)性消息組件, 為了保證領(lǐng)域事件一定被發(fā)送出去,就需要依賴本地事務(wù)表。我們將領(lǐng)域?qū)ο蟊4婧皖I(lǐng)域事件發(fā)布任務(wù)記錄在一個事務(wù)中得以執(zhí)行。在領(lǐng)域事件推送消息中間件MQ中,在數(shù)據(jù)庫保存完畢后,先主動發(fā)送一次(容許失?。?,如果發(fā)送失敗再等待定時調(diào)度掃描事件表重新發(fā)送。如下圖所示:

一般情況下,領(lǐng)域事件都是在業(yè)務(wù)操作的時候產(chǎn)生,此時我們將領(lǐng)域事件暫存到注冊中心。待入庫的時候,在一個事務(wù)包裹中進(jìn)行保存。發(fā)布者如下所示,如果聚合根需要使用此發(fā)布者事件注冊服務(wù),只需要實現(xiàn)此Publisher接口即可。因為內(nèi)部使用了WeakHashMap 作為容器,如果當(dāng)前對象不再被應(yīng)用,之前注冊的事件列表會被自動回收掉。

/**
 * 描述:發(fā)布者接口
 *
 */
public interface Publisher {
    /**
     * 容器
     */
    Map<Object, List<DomainEvent>> container = Collections.synchronizedMap(new WeakHashMap<>());

    /**
     * 注冊事件
     *
     * @param domainEvent
     */
    default void register(DomainEvent domainEvent) {
        List<DomainEvent> domainEvents = container.get(this);
        if (Objects.isNull(domainEvents)) {
            domainEvents = Lists.newArrayListWithCapacity(2);
            container.put(this, domainEvents);
        }
        domainEvents.add(domainEvent);
    }

   /**
     * 獲取事件列表
     *
     * @return
     */
    default List<DomainEvent> getEventList() {
        return container.get(this);
    }
  
 // 更多代碼...略
  
}


簡化方案

如果一些簡單的應(yīng)用,不需要使用MQ消息隊列進(jìn)行事件中轉(zhuǎn),也可以將本地事件表的發(fā)送狀態(tài)作為任務(wù)處理狀態(tài)。這樣可以簡化一些網(wǎng)絡(luò)開銷,如在一個應(yīng)用內(nèi),借助guava的EventBus組件完成消息發(fā)布-訂閱機(jī)制。即簡化為:訂閱處理器如果全部執(zhí)行成功,才更新消息表為已發(fā)送(也可以認(rèn)為是已執(zhí)行)。

在實際開發(fā)中,實際上我們很多領(lǐng)域事件都是基于此簡化方案進(jìn)行處理的,因領(lǐng)域事件的部分處理功能簡單,使用簡化方案能節(jié)省很多開發(fā)時間和代碼量。

3.3.6 SAGA事務(wù)

概述

采用DDD之后,雖然還是可以從應(yīng)用層采用基礎(chǔ)的事務(wù)性編程保證本地數(shù)據(jù)庫的事務(wù)性。然而當(dāng)處于微服務(wù)架構(gòu)模式,我們的業(yè)務(wù)常常需要多個跨應(yīng)用的微服務(wù)協(xié)同,采用事務(wù)進(jìn)行一致性保證就顯得鞭長莫及。

即使不采用DDD編程, 我們過往已經(jīng)開始采用Binlog(MySQL的主從同步機(jī)制)或者事務(wù)性消息來實現(xiàn)最終一致性。在越來越流行的微服務(wù)架構(gòu)趨勢下(應(yīng)用資源的分布式特性),通過傳統(tǒng)的事務(wù)ACID(atomicity、consistency、isolation、durability)保證一致性已經(jīng)很難,現(xiàn)在我們通過犧牲原子性(atomicity)和隔離性(Isolation),轉(zhuǎn)而通過保證CD來實現(xiàn)最終一致性。

解決分布式事務(wù),有許多技術(shù)方案如:兩階段提交(XA)、TCC、SAGA。

關(guān)于分布式事務(wù)方案的優(yōu)缺點,有很多論文和技術(shù)文章,為什么選擇SAGA ,正如 Chris Richardson在《微服務(wù)架構(gòu)設(shè)計模式》中所述:

  1. XA對中間件要求很高,跨系統(tǒng)的微服務(wù)更是讓XA鞭長莫及;XA和分布式應(yīng)用天生不匹配;

  2. TCC 對每一個參與方需要實現(xiàn)(Try-confirm-cancel)三步,侵入性較大;

  3. SAGA是一種在微服務(wù)架構(gòu)中維護(hù)數(shù)據(jù)一致性的機(jī)制,它可以避免分布式事務(wù)帶來的問題。通過異步消息來協(xié)調(diào)一系列本地事務(wù),從而維護(hù)多個服務(wù)直接的數(shù)據(jù)一致性;

  4. SAGA理論部分, 可以參考:分布式事務(wù):SAGA模式和Pattern: Saga

SAGA 理論

1987年普林斯頓大學(xué)的Hector Garcia-Molina和Kenneth Salem發(fā)表了一篇Paper Sagas,講述的是如何處理long lived transaction(長活事務(wù))。Saga是一個長活事務(wù)可被分解成可以交錯運(yùn)行的子事務(wù)集合。其中每個子事務(wù)都是一個保持?jǐn)?shù)據(jù)庫一致性的真實事務(wù)。 論文地址:sagas

Saga的組成

  • 每個Saga由一系列sub-transaction Ti 組成; (每個Ti是保證原子性提交);

  • 每個Ti 都有對應(yīng)的補(bǔ)償動作Ci,補(bǔ)償動作用于撤銷Ti造成的結(jié)果; (Ti如果驗證邏輯且只讀,可以為空補(bǔ)償,即不需要補(bǔ)償);

  • 每一個Ti操作在分布式系統(tǒng)中,要求保證冪等性(可重復(fù)請求而不產(chǎn)生臟數(shù)據(jù));

    Saga的執(zhí)行順序有兩種:

    • T1, T2, T3, ..., Tn (理想狀態(tài),直接成功);

    • T1, T2, ..., Tj, Cj,..., C2, C1,其中0 < j < n (向前恢復(fù)模式,一般為業(yè)務(wù)失?。?;

Saga補(bǔ)償示例: 如果在一個事務(wù)處理中,Ti為發(fā)郵件, Saga不會先保存草稿等事務(wù)提交時再發(fā)送,而是立刻發(fā)送完成。 如果任務(wù)最終執(zhí)行失敗, Ti已發(fā)出的郵件將無法撤銷,Ci操作是補(bǔ)發(fā)一封郵件進(jìn)行撤銷說明。

SAGA有兩種主要的模式,協(xié)同式、編排式。

A 事件協(xié)同式SAGA(Event choreography)

把Saga的決策和執(zhí)行順序邏輯分布在Saga的每個參與方中,他們通過相互發(fā)消息的方式來溝通。

在事件編排方法中,第一個服務(wù)執(zhí)行一個事務(wù),然后發(fā)布一個事件,該事件被一個或多個服務(wù)進(jìn)行監(jiān)聽,這些服務(wù)再執(zhí)行本地事務(wù)并發(fā)布(或不發(fā)布)新的事件。當(dāng)最后一個服務(wù)執(zhí)行本地事務(wù)并且不發(fā)布任何事件時,意味著分布式事務(wù)結(jié)束,或者它發(fā)布的事件沒有被任何 Saga 參與者聽到都意味著事務(wù)結(jié)束。

① 優(yōu)點:

  • 避免中央?yún)f(xié)調(diào)器單點故障風(fēng)險;

  • 當(dāng)涉及的步驟較少服務(wù)開發(fā)簡單,容易實現(xiàn);

② 缺點:

  • 服務(wù)之間存在循環(huán)依賴的風(fēng)險;

  • 當(dāng)涉及的步驟較多,服務(wù)間關(guān)系混亂,難以追蹤調(diào)測;

  • 參與方需要彼此感知上下耦合關(guān)聯(lián)性,無法做到服務(wù)單元化;

B 命令編排式SAGA(Order Orchestrator)

中央?yún)f(xié)調(diào)器(Orchestrator,簡稱 OSO)以命令/回復(fù)的方式與每項服務(wù)進(jìn)行通信,全權(quán)負(fù)責(zé)告訴每個參與者該做什么以及什么時候該做什么。

① 優(yōu)點:

  • 服務(wù)之間關(guān)系簡單,避免服務(wù)間循環(huán)依賴,因為 Saga 協(xié)調(diào)器會調(diào)用 Saga 參與者,但參與者不會調(diào)用協(xié)調(diào)器。

  • 程序開發(fā)簡單,只需要執(zhí)行命令/回復(fù)(其實回復(fù)消息也是一種事件消息),降低參與者的復(fù)雜性。

  • 易維護(hù)擴(kuò)展,在添加新步驟時,事務(wù)復(fù)雜性保持線性,回滾更容易管理,更容易實施和測試。

② 缺點:

  • 中央?yún)f(xié)調(diào)器處理邏輯容易變得龐大復(fù)雜,導(dǎo)致難以維護(hù)。

  • 存在協(xié)調(diào)器單點故障風(fēng)險。

命令編排式SAGA示例—— 非訂單聚合提票開票申請

Saga在發(fā)票開票申請的案例如下所示,提票申請被拆分為2個主要的SAGA協(xié)調(diào)器。

① 在接收到【母申請單已經(jīng)創(chuàng)建事件】即觸發(fā)生成協(xié)調(diào)器1調(diào)度——開票申請SAGA協(xié)調(diào)器, 用于參數(shù)驗證、訂單鎖定、占用應(yīng)開金額和數(shù)量、最后按開票規(guī)則拆分為多個子申請單(一個子申請單對一張實際的發(fā)票)。在多個子申請單完成創(chuàng)建后, 會發(fā)布【子申請單已創(chuàng)建】事件。

② 在接收到【子申請單已經(jīng)創(chuàng)建事件】即觸發(fā)生成協(xié)調(diào)器2調(diào)度——子申請單提票SAGA協(xié)調(diào)器, 用于子申請單預(yù)占流水記錄、提交財務(wù)開票、接收財務(wù)狀態(tài)同步子申請單狀態(tài)。

? 使用編排式Saga, 對每一個步驟的調(diào)用也不一定是同步的,也可以發(fā)送處理請求后掛起協(xié)調(diào)處理器,等待異步消息通知。通過消息中間件如MQ收到某個步驟的處理結(jié)果消息,然后再恢復(fù)協(xié)調(diào)器的繼續(xù)調(diào)度。假設(shè)Saga事務(wù)的每個步驟都是異步的,那么編排式協(xié)調(diào)器和事件協(xié)調(diào)器就非常類同,唯一的好處是整個業(yè)務(wù)處理的消息收發(fā)均要通過Saga協(xié)調(diào)器作為中樞。當(dāng)前在哪一步驟,下一步要做什么可以由SAGA協(xié)調(diào)器統(tǒng)一支配。

? 對于一個比較復(fù)雜的長活事務(wù),從業(yè)務(wù)的完整性和排查問題的方便性考慮,我們推薦使用Saga編排式事務(wù)來收斂業(yè)務(wù)的調(diào)度復(fù)雜度,以免在消息發(fā)送接收網(wǎng)絡(luò)中迷失。編排式事務(wù)有時候類似一個狀態(tài)機(jī),當(dāng)前任務(wù)執(zhí)行到哪個步驟,哪個狀態(tài)能夠被保存和復(fù)原,且條理性更加清晰。

? 在編排式Saga事務(wù)中,我們需要使用到eventSource類似的事件記錄,以便記錄每一個步驟的執(zhí)行情況和部分上下文信息。除了手動建表之外(目前我們采用的方案),也有很多成熟的框架可供選擇,如:alibaba的seata,微服務(wù)架構(gòu)設(shè)計模式推薦的eventuate 。

風(fēng)險:

當(dāng)然在使用saga中,還需要考慮隔離性缺失帶來的風(fēng)險,尤其是在交易和金融環(huán)節(jié)。這不是saga能直接解決的問題,這需要通過語義鎖(未提交數(shù)據(jù)加字段鎖,防止臟讀)、交換式更新、版本文件、重讀值等方案進(jìn)行處理。

4. 參考資料

4.1 參考書籍

Domain-Driven Design《領(lǐng)域驅(qū)動設(shè)計》--Eric Evans

MicroServices Patterns《微服務(wù)架構(gòu)設(shè)計模式》 -- Chirs Richardson

《DDD 實戰(zhàn)課》 -- 歐創(chuàng)新

_4.2_網(wǎng)絡(luò)資料

領(lǐng)域模型核心概念:實體、值對象和聚合根

聚合(根)、實體、值對象精煉思考總結(jié)

DDD(Domain-Driven Design)領(lǐng)域驅(qū)動設(shè)計在互聯(lián)網(wǎng)業(yè)務(wù)開發(fā)中的實踐

DDD落地實踐

https://www.jianshu.com/p/91bfc4f21caa

https://www.jianshu.com/p/4a0d89dd7c20

領(lǐng)域驅(qū)動設(shè)計(2) 領(lǐng)域事件、DDD分層架構(gòu)

https://my.oschina.net/lxd6825/blog/5485465

saga分布式事務(wù)_本地事務(wù)和分布式事務(wù)-tencent

作者:京東零售?張世彬

來源:京東云開發(fā)者社區(qū) 轉(zhuǎn)載請注明來源文章來源地址http://www.zghlxwxcb.cn/news/detail-745906.html

到了這里,關(guān)于DDD技術(shù)方案落地實踐的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實不符,請點擊違法舉報進(jìn)行投訴反饋,一經(jīng)查實,立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費用

相關(guān)文章

  • 【實踐篇】手把手教你落地DDD

    【實踐篇】手把手教你落地DDD

    常見的DDD實現(xiàn)架構(gòu)有很多種,如經(jīng)典四層架構(gòu)、六邊形(適配器端口)架構(gòu)、整潔架構(gòu)(Clean Architecture)、CQRS架構(gòu)等。架構(gòu)無優(yōu)劣高下之分,只要熟練掌握就都是合適的架構(gòu)。本文不會逐個去講解這些架構(gòu),感興趣的讀者可以自行去了解。 本文將帶領(lǐng)大家從日常的三層架構(gòu)

    2024年02月06日
    瀏覽(93)
  • DDD領(lǐng)域驅(qū)動設(shè)計(六)

    領(lǐng)域?qū)ο笮枰Y源存儲。存儲手段多樣化,常見就是數(shù)據(jù)庫,分布式緩存,localCache.資源庫的作用,就是對領(lǐng)域的存儲和訪問進(jìn)行統(tǒng)一管理對象。在抽獎平臺中。通過下面這種方式組織資源庫。

    2024年01月24日
    瀏覽(22)
  • DDD領(lǐng)域驅(qū)動

    DDD領(lǐng)域驅(qū)動

    我們經(jīng)常講技術(shù)為業(yè)務(wù)服務(wù),架構(gòu)設(shè)計需要對業(yè)務(wù)充分理解,在面向復(fù)雜的業(yè)務(wù)場景時,會面臨諸多問題: 復(fù)雜系統(tǒng)設(shè)計 :業(yè)務(wù)系統(tǒng)多、業(yè)務(wù)類型多、業(yè)務(wù)相互耦合,有沒有合適的方法來指導(dǎo)模塊的邊界開發(fā)? 多團(tuán)隊協(xié)同 :業(yè)務(wù)系統(tǒng)邊界劃分不清,系統(tǒng)間依賴復(fù)雜,往往一

    2024年02月09日
    瀏覽(20)
  • DDD[領(lǐng)域驅(qū)動模型]

    DDD[領(lǐng)域驅(qū)動模型]

    這是一種思想,不是一個工具。更多內(nèi)容前往 IT-BLOG Eric Evans 于 2004 年提出的一種軟件設(shè)計方法和理論。在應(yīng)用架構(gòu)的設(shè)計中, 領(lǐng)域驅(qū)動設(shè)計 DDD 占據(jù)著非常重要的位置,可以說 DDD 是應(yīng)用架構(gòu)設(shè)計的核心。 DDD 是一套綜合軟件系統(tǒng)分析和設(shè)計的面向?qū)ο蠼7椒ā?過去 系統(tǒng)

    2024年02月04日
    瀏覽(23)
  • 領(lǐng)域驅(qū)動設(shè)計DDD架構(gòu)解析和繪圖模板分享

    領(lǐng)域驅(qū)動設(shè)計DDD架構(gòu)解析和繪圖模板分享

    DDD整潔架構(gòu) DDD整潔架構(gòu)為了解決強(qiáng)調(diào)用的關(guān)系,出現(xiàn)了 洋蔥架構(gòu)(六邊形)架構(gòu) ,就是為了實現(xiàn) 依賴倒置 它的思想就是把領(lǐng)域模型放到核心的位置, 領(lǐng)域模型 是獨立的,不會直接強(qiáng)依賴其他層,而通過 適配器 來完成領(lǐng)域模型和外層的數(shù)據(jù)交換。 DDD分層架構(gòu)和三層架構(gòu)的

    2024年02月05日
    瀏覽(29)
  • 快速理解DDD領(lǐng)域驅(qū)動設(shè)計架構(gòu)思想-基礎(chǔ)篇

    本文與大家一起學(xué)習(xí)并介紹領(lǐng)域驅(qū)動設(shè)計(Domain Drive Design) 簡稱DDD,以及為什么我們需要領(lǐng)域驅(qū)動設(shè)計,它有哪些優(yōu)缺點,盡量用一些通俗易懂文字來描述講解領(lǐng)域驅(qū)動設(shè)計,本篇并不會從深層大論述講解落地實現(xiàn),這些大家可以在了解入門后再去深層次學(xué)習(xí)探討或在后續(xù)進(jìn)階

    2024年02月10日
    瀏覽(22)
  • DDD進(jìn)階_領(lǐng)域事件是什么?如何開展領(lǐng)域事件驅(qū)動開發(fā)工作?

    DDD進(jìn)階_領(lǐng)域事件是什么?如何開展領(lǐng)域事件驅(qū)動開發(fā)工作?

    DDD從入門到精通,系列文章傳送地址,請點擊本鏈接。 ? 目錄 一、什么是領(lǐng)域事件 二、如何識別領(lǐng)域事件 三、領(lǐng)域事件的數(shù)據(jù)一致性 四、領(lǐng)域事件分類 1、微服務(wù)內(nèi)的領(lǐng)域事件 2、微服務(wù)之間的領(lǐng)域事件 五、領(lǐng)域事件案例 六、領(lǐng)域事件總體架構(gòu)圖 1. 事件構(gòu)建和發(fā)布 2、事

    2024年02月15日
    瀏覽(20)
  • 【架構(gòu)】領(lǐng)域驅(qū)動設(shè)計(DDD)的幾種典型架構(gòu)介紹

    【架構(gòu)】領(lǐng)域驅(qū)動設(shè)計(DDD)的幾種典型架構(gòu)介紹

    我們生活中都聽說了DDD,也了解了DDD,那么怎么將一個新項目從頭開始按照DDD的過程進(jìn)行劃分與架構(gòu)設(shè)計呢? 各種服務(wù) IAAS:基礎(chǔ)設(shè)施服務(wù),Infrastructure-as-a-service PAAS:平臺服務(wù),Platform-as-a-service SAAS:軟件服務(wù),Software-as-a-service 從圖中已經(jīng)可以很容易看出架構(gòu)的演進(jìn)過程,

    2024年02月11日
    瀏覽(17)
  • 萬字長文助你上手軟件領(lǐng)域驅(qū)動設(shè)計 DDD

    萬字長文助你上手軟件領(lǐng)域驅(qū)動設(shè)計 DDD

    最近看了一本書《解構(gòu)-領(lǐng)域驅(qū)動設(shè)計》,書中提出了領(lǐng)域驅(qū)動設(shè)計統(tǒng)一過程(DDDRUP),它指明了實踐 DDD 的具體步驟,并很好地串聯(lián)了各種概念、模式和思想。因此,我對書本內(nèi)容做了梳理、簡化,融入自己的理解,并結(jié)合之前閱讀的書籍以及實踐經(jīng)驗,最終形成這篇文章。

    2024年02月08日
    瀏覽(23)
  • 軟件架構(gòu)演進(jìn)過程與微服務(wù)設(shè)計中的領(lǐng)域驅(qū)動設(shè)計(DDD)

    軟件架構(gòu)的演進(jìn)是一個不斷改進(jìn)和解決問題的過程。從傳統(tǒng)架構(gòu)到面向服務(wù)架構(gòu)(SOA),再到微服務(wù)架構(gòu),每個階段都帶來了新的技術(shù)和解決方案。而在微服務(wù)架構(gòu)中,領(lǐng)域驅(qū)動設(shè)計(DDD)起著至關(guān)重要的作用,它能夠提高系統(tǒng)的可擴(kuò)展性、可維護(hù)性和可理解性。本文將介紹軟件架

    2024年02月16日
    瀏覽(19)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包