實現領域驅動設計之感悟(一)

2022-06-30 00:51:12 字數 3943 閱讀 6681

接觸領域驅動設計的概念,已有4年了。從看書了解的純理論,到實際專案應用中遇到建模問題的思考,逐漸提公升了建模能力。正好碰到2023年五一放假,想趁這個機會,寫一下我的學習感悟。

公司內的業務沉澱達到一定量,現有老系統維護困難,這個時候,有必要引入領域驅動設計,在這裡簡稱ddd。

產品經理的業務設計和最終產品實現出入比較大,往往功能看似一樣,實質在業務變更時會難以實現。解決辦法是讓產品經理參與進來,在軟體建模期間和開發人員保持一致,以降低最終實現和業務設計的差異。

後續會分別從了解領域的概念、領域建模,到實戰開發,逐步講解清楚。

我理解為是所處的行業,譬如電商行業、製造業、運輸業,等等等等。也可以是細分的,如零售行業、汽車製造行業。那麼這些,可以稱之為領域。

在特定領域下,通過產品經理和業務人員的共同努力,針對面臨的業務問題,進行解決。已解決的部分,就是解決方案空間(需求);尚未解決的,就是問題空間(潛在需求)。

對於領域專家(產品經理)的職責:

對於開發人員的職責:

子域:在解決方案空間中,將領域細分成各個子業務,每個子業務稱之為子域。

通用子域:對於和業務關聯性不大的子域,具有可替代性的,稱之為通用子域。如許可權管理系統,初期可以通過購買產品以節省人力成本。

支撐子域:子域之間,存在乙個依賴關係,作為當前子域的上游子域,稱之為支撐子域。如產品目錄子域,是訂單子域的支撐子域。

核心域:對於公司,具有核心競爭力的業務,對應的子域,即為核心域。公司發展的不同時期,核心域也會隨之改變。(原來的核心域,可能會變成新的核心域的支撐子域)

限界上下文:乙個業務系統,稱之為限界上下文。如erp系統,就是個限界上下文。老的erp系統改造前,會涵蓋所有子域。經過按子域進行剝離後,被剝離的子域,將擁有獨立的限界上下文。如erp裡的訂單模組、商品管理模組、倉庫管理模組。被剝離後,變成訂單系統、商品中心、倉庫管理系統。

通用語言:在當前限界上下文,建模時溝通用的語言,稱為通用語言,是開發人員與領域專家(產品經理就是)之間的橋梁。如同大家坐在一起討論特定主題時,都是圍繞主題發言,涉及的名詞也是在這個語境下進行,不會產生理解分歧問題。

通用語言中使用的名詞,一般由行業專業術語 + 公司特有業務術語組成;僅作用於當前的限界上下文,具有無歧義性的特點;用於描述業務物件屬性及行為、業務場景。

限界上下文對映:多個限界上下文之間存在互動,為了直觀表達之間的關係,我們需要一張系統互動關係圖,來表達上下游之間的關係,這就是限界上下文對映。

我將分別按組織模式、整合模式,進行討論。此處的限界上下文的整合,我們可以簡稱為系統整合。

組織模式,即團隊間的關係。上下游團隊配合度高的組織,就當前限界上下文而言,自然整合的代價是最小的。

架構,是用於解決非功能性需求而提出的一系列解決方案。以下的架構,在實際應用中,不是單獨存在的,往往乙個系統會採用幾種架構的組合。

好的架構,在於選型是否合適,而非架構本身的優劣。

所有架構的始祖。支援n層架構系統,被廣泛應用於web、企業級應用。傳統的分層架構中,往往基礎設施作為依賴關係的最底層。隨著依賴注入(di)的出現,通過依賴倒置原則,在領域驅動設計上,可以將領域層,作為整個專案的依賴最底層。目的呢,是為了讓領域層不依賴基礎設施層,從而在建模期間,可以將重心放在建模本身。

服務端開放埠,對外提供服務;客戶端通過介面卡,使得可以使用服務端的服務。之前講限界上下文對映時提到的整合模式中的防腐層,在此處即為介面卡的實現。

這是ddd的首選架構,即主架構。

基於webservice、wcf的面向服務的開發。這塊目前正逐漸被微服務架構所取代,就不細講了。

通過http restful、mq訊息佇列協議,實現服務之間互動。此架構的優勢在於可以水平拆分無狀態服務,實現可伸縮性、高可用性。乙個限界上下文,可以被拆分成多個微服務。

將資料修改和資料查詢進行分離,天然得支援讀寫分離。優勢在於可以分別從c端、q端優化資料結構,提高系統的可用性。

c端的使用,每次的資料修改,會伴隨領域事件的發布,通過對領域事件的訂閱,完成q端資料的更新。

如果q端的資料是非同步更新的,那麼會存在資料延遲重新整理問題,其實這個問題在所有讀寫分離的使用場景都會存在,解決辦法也多種多樣。如客戶端增加延遲重新整理頁面、頁面重新整理時顯示最後一次q端資料更新的時間,以消除使用者疑惑。

通過事件、事件處理器,來驅動系統的執行。

此架構在實際應用場景,考慮到記憶體訊息的易失性和服務集群部署的特點,往往會引入訊息中介軟體。

如果用管道來模擬的話:事件作為管道的輸入;事件處理器作為管道,來處理事件;管道的輸出,以新的事件的形式發布,再由關注此類事件的事件處理器進行處理。

這些事件處理,可以序列處理,也可以並行處理(當單個處理比較耗時,且相互之間不需要等待處理結果,可以並行,以縮短整體響應時長)。

模擬工作流:接收到特定的事件,作為流程中節點的流轉的條件;直到整個流程結束,那麼這個長時處理過程,即完成。

使用場景:多聚合根之間互動的最終一致性實現;跨限界上下文互動的最終一致性實現(屬於分布式最總一致性範疇)。

記錄每次導致領域物件發生變化的事件。隨著人們要求更多的變化跟蹤資訊,在業務層也需要記錄相應量的元資料資訊。特別像財務這些對於金額變化比較敏感的業務,當需要引入審計功能時,對於具體發生金額變化的原始資訊需要記錄。那麼引入事件源的概念,就再適合不過。

事件源資料,是只追加型方式儲存的。隨著時間推移,資料量會越來越大,對於頻繁訪問的業務物件,也會產生效能影響。通過引入事件快照,可以解決此類效能問題。

資料量的增大,可以採用支援分布式儲存的儲存引擎,如mongodb,或者對基於關係型資料庫實現的分庫分表,以解決資料量大的問題。

簡單來講,通過引入本地快取、分布式快取的概念,將經常訪問的業務物件,儲存其中。將業務物件持久化到快取中,相對關係型資料庫表而言,要容易的多。此處的業務,特指聚合根物件,因此物件快取,我們也可以稱之為聚合儲存(aggregate store)。

如分布式大資料的商品產品gemfire,支援資料複製、持續查詢、分布式處理。

將此概念結合cqrs使用,可以最大化提公升c端的效能。

使用 「乙個快取對應乙個聚合」的策略時,如果這個快取存在於乙個分布式快取節點,那麼會存在單點問題。引入多節點(冗餘節點)時,需要考慮多節點之間的資料複製問題,以確保資料同步。

可以很好結合事件驅動架構。通過發布領域事件,到支援按事件更新分布式快取;分布式快取過期,由資料網織服務發布「重新初始化快取」事件,再由業務系統通過訂閱後,進行快取更新。這種相對傳統的完整更新快取而言,網路資料傳輸量可以降低。

有些資料網織支援一種名為持續查詢(continuous query)的事件通知。客戶端向資料網織註冊乙個查詢,當對快取進行修改時,客戶端可以收到事件通知,結合業務本身來判斷是否需要通知使用者介面元件。

可以在所有快取節點範圍內完成分布式處理,然後將處理結果聚合到一起發給客戶端。如事件驅動架構裡提到的長時處理過程,可以將幾個並行的事件處理轉為乙個函式服務(function service),註冊到資料網織中,最終結果將以事件形式發布。

用我熟悉的電商領域中的庫存子域作為核心域,來列舉幾個業務場景:

4)將發貨商品一樣的,或發貨商品所在同乙個揀貨子區域的出貨單,合併成一張揀貨總單,以提高揀貨效率

5)倉庫揀貨人員在揀貨區域,按照揀貨總單上的單號、存放商品的盒子條碼進行下貨,並告知揀貨總單下的各個出貨單需揀貨的數量,完成多個出貨單的揀貨過程。

問題:就以上的業務場景,列一下涉及哪幾個子域,子域之間的上下游關係?哪些是支撐子域,哪些是核心域,哪些是通用子域。

好了,這篇講了ddd的戰略內容大部分,之所以先將這些,是因為這些是開發人員和產品經理都應該知道的,而且能大致了解ddd裡都有哪些東西。

領域驅動設計系列(一) 為何要領域驅動設計?

領域驅動設計最近貌似開始火起來了,越來越多的人開始認識到領域設計的重要性,從我做過的專案來看,似乎歐洲已經有很多的公司開始實施領域驅動設計了,我看領域驅動設計也有些時間了,但是網上不管是文章還是 都顯得太過 高大上 一談領域驅動設計,一大堆的概念一股腦的給你上上來,搞的有點暈頭轉向,而我想在一些中小...

《實現領域驅動設計》筆記

1 不要用貧血物件 雖然do是貧血的,但目前的do實際是dataobject,domainservice是實際的domainobject 2 多跟領域專家溝通 3 計費核心域為計費執行 1 計費執行包括計費條件 計費過程 計費結果 2 建立計費上下文,如計費時間 4 應用服務應當是無狀態的 5 分層...

如何領域驅動設計? 實踐感悟 總結分享

主要是在開發過程中,個人對於領域驅動設計的實踐感悟和總結 也是對新進開發人員的培訓資料 希望對關注ddd的童鞋有所幫助。領域驅動不是純粹的技術問題,領域建模 建立資料表只是一部分 是領域專家 客戶 產品團隊 和開發人員溝通努力 抽象的的結果。領域建模的目的是,經過有效的溝通 詳細分析 良好設計可以更...