<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      【讀書筆記】【設計】實現領域驅動設計(DDD)筆記

      前言:

        領域驅動設計,是一種架構思想,它不是關于技術的,而是關于討論、聆聽、發現和業務價值的,而這些都是為了把知識挖掘并表達出來。

        敏捷開發:DDD并非充滿繁文縟節的笨重的開發過程,相反它可以和敏捷很好的結合。可以采用“測試先行、逐步改進”的設計思路。其中重構是最必要的一步。

      領域:

        領域分類:可以劃分為核心子域、支撐子域、通用子域。核心域是值得配置最好的開發者的,但是有時候子域在自己眼里也是自己的核心域。所以核心是相對而言的,相對整個BU來說,也會有自己賺錢的核心域。

      • 核心域擁有核心的知識抽象,使用DDD戰術回大有裨益。不過DDD戰術同樣適用在其他域中。
      • 如果你不知道怎么劃分界限上下文的時候,不如先描述核心域,然后創建一個核心上下文。

        問題空間:問題空間是核心域和其他子域的組合。問題空間中的子域通常隨著項目的不同而不同,他們各自關注于當前的業務。

        解決方案空間:解決方案空間包含一個或者多個界限上下文,所以說一個界限上下文對應的是一個解決方案,解決方案可以是演進的。

        界限上下文:界限上下文是一個顯式的邊界,領域模型便存在于這個邊界當中,領域語言把通用模型(語言)表達為軟件模型。每一個模型概念,包括它的屬性和操作,在邊界之內都具有特殊的含義。但界限上下文不應該只包含通用語言和領域模型,它還包括那些提供交互手段,輔助功能的內容,例如數據庫,UI,REST資源等等。但如果數據庫不是一個Context專用,而是要求通用多系統共享,或許會淡出一個上下文。

      • 它對應了一組解決方案,一個會議可能會形成一個上下文,產生一個解決方案。
      • 好的設計應該是每一個子領域內應該形成自己的子域界限上下文的,不然的話可能你的子域可能劃分的不太好。然后一個大領域下就會存在多個不同的界限上下文。——(P49)
      • 在一個好的界限上下文中,一個術語應該值表達一種領域概念,例如顧客在不同上下文中含義就不同,所以應該在不同上下文中讓顧客更佳實體化,例如在會員系統中顧客就是:會員顧客
      • 上下文和物理結構不一定要是一一對應的
      • 界限上下文是可以在重構中產生的。

        通用語言:通常來說,通用語言和界限上下文存在一一對應的關系。

      • 界限上下文的通用語言,向我們提供了設計領域模型的概念術語,用它來指導實體建模、領域服務、值對象設計。
      • 通用語言應該直接反映在代碼中,而要保持設計文檔的實時更新是非常困難而且不可能的。

        戰略重要性:戰略設計基本會劃分大大小小的各個子域及上下文,但是有些情況下一個新的子域或許不會那么明顯,那么在知識沉淀的時候會發現矛盾所在,所以上下文也有可能新增和重整。這些都是會上升到戰略層的決定。如果不重構矛盾到子域,可能會導致一些大泥球的設計。

      上下文映射圖:

        上下文映射圖:這些不同的上下文中的模型會有重疊的本質物理實體,但表現出了不同的領域模型。這個時候就需要為他們的映射關系進行管理,最有效的手段就是上下文映射圖。

      • 上下文映射圖為什么重要,因為它幫我我們在解決方案空間中看待問題

        上下文關系:上下文的關系在映射圖中可以表示,一般會有上游和下游之間的關系,其中設計關系的概念如下:

      • 合作關系:一起成功、一起失敗。
      • 共享內核:兩個上下文共享內核,有利有弊,但要保證內核的持續集成發展;
      • 客戶方-供應方關系:上游不依賴下游,下游依賴上游;
      • 遵奉者:上下游中,上游不幫助下游,擺爛的關系;
      • 防腐層:當關聯上下文難以正確映射的時候,需要一個單獨的層作為代理做這件事,提供模型翻譯功能;
      • 開放主機服務:上下文的公開協議;
      • 發布語言:兩個界限上下文的翻譯語言;
      • 另謀他路(SeparateWay):聲明兩個上下文不存在關系;
      • 大泥球(Big Ball Of Mud):混在一起的模型,邊界不清晰,就把他們整體混成一個大泥球;

        個人實踐:物流單是一個上下文(整體物流上下文),作業單分別有自己的上下文(作業上下文),作業單代表的是作業域,與實操交互;物流單是協同域,與協同交互。

      架構:

        分層:大致的層次有用戶接口層、應用層、領域層、基礎設施層;一般他們的關系是:用戶接口層->應用->領域層->基礎設施層;

      • 通過依賴導致,把基礎設施層導致依賴領域層,其他不用過多關系,但是領域層應該不被依賴,作為穩定的核心。

        六邊形架構:也稱之為端口適配器,對于每一種外界類型,都有一個適配器與之相對應。

        面向服務的架構:面向服務架構,以服務為主。可以用六邊形架構結合,利用REST和SOAP等適配器提供服務。

      • 服務契約、松耦合、服務抽象、服務重用性、服務自治性、服務無狀態性、服務可發現、服務組合性(P114)
      • 不應該被技術服務影響上下文模型的大小和劃分,業務價值高于技術策略、戰略目標高于項目利益。

        RESTful架構:要區分架構風格和架構,架構風格是將所有架構共有的東西抽象出來。REST本來就應該是屬于WEB架構的一種架構風格。

      • 第一是資源,資源是具有展現(representation)和狀態(state)的
        • 展現的格式可能不同xml\json\hetm
        • 資源應該有URI進行唯一定義
        • 資源不是獨立存在的,是不同資源連在一起的,這也是WEB的本質。
      • 第二是接口,把資源看成對象,它應該有什么暴露的方法接口
      • 與DDD結合:這個問題簡單,我們不應向外暴露領域模型,所以推薦創建一個單獨的上下文,來訪問領域模型,包裝為資源向外暴露。

        CQRS:查詢與命令職責分離

      • 命令:如果一個方法修改了對象狀態,那么它是一個命令;
      • 查詢:如果一個方法返回了數據,不改狀態,那么它是一個查詢;

        有了命令和查詢的區分,那么便有了查詢處理器和命令處理器,這些處理器操作的對應的模型如下:

      • 查詢模型:可以創建多個視圖作為查詢模型。查詢模型應該是監聽領域事件來生成;
      • 命令模型:當命令處理器執行后,一個聚合應該會被更新,同時可能會發布領域事件;
        • 其他聚合可能會監聽事件,然后相應更改,達到最終一致性;
        • 查詢模型會監聽事件

        事件驅動架構

        長時處理過程(Saga):Long-Runing Process,將處理過程設計成為一組聚合,這些聚合在一系列的活動中相互協作。一個或者多個聚合實例充當任務執行組件并維護整個處理過程的狀態。

      • 執行器和跟蹤器:用聚合作為跟蹤器處理跟蹤長時處理過程的狀態。我們不需要一個專門的跟蹤器來做這種事。
        • 在實際的領域中,一個長時處理過程的執行器將創建一個新的類似聚合的狀態對象來跟蹤事件的完成情況。
        • 長時處理過程通常有一個isComplete的方法,返回true的時候,執行器將根據領域需要發布領域事件。
      • 事件源:對于某個聚合上的每次命令操作,都有至少一個領域事件發布出去,該事件描述了操作的執行結果。每一次領域事件都將被保存到Event Store中。
        • 重建:從資源庫獲取聚合的時候,我們可以通過事件源的歷史事件來重建聚合。
        • 有序:事件應該是有順序的重放。
        • 快照:領域事件太多的時候,重建會有性能問題,所以這個時候可以用“快照”的方法。

      實體:

        通常因為基于數據庫的映射,會有很多getter setter方法,但這不是ddd的做法。

        唯一的身份標識和可變性特征可以區分實體和值對象。

        唯一標識:創建實體唯一標識的策略

      • 用戶提供的輸入作為唯一標識,程序嚴重唯一性
      • 程序內部提供唯一標識,如UUID,分布式ID
      • 數據庫幫忙生成唯一標識
      • 另一個界限上下文已經決定了可以用的標識作為輸入

        標識生成時機:實體唯一標識可以在對象創建的時候,也可以在持久化的時候,這要看這個標識在創建過程是否重要,例如創建需要發送領域事件的場景,則需要提前創建;

      • 委派標識:就是數據庫id,但最好別透露出去給Domain,擁有這個標識也是重要的

        發現實體的本質特征:界限上下文的通用語言,向我們提供了設計領域模型的概念術語。

        跟蹤變化:跟蹤實體變化最有用的方法就是領域事件和事件存儲;

      值對象:

        我們盡量使用值對象來建模而不是實體對象,因為我們可以非常容易地對值對象進行創建、測試、使用、優化和維護。

        通用語言:通用語言是值對象的首要原則;值對象通常有以下特征

      • 度量、描述:它量度或者描述了領域中的一件東西
      • 不變性:它可以作為不變量
      • 整體概念:它將不同的相關的屬性組合成一個概念整體,每一個屬性對于概念都不可或缺,它和把這些值組裝在一起時不同的
      • 可替換性:它可以和其他值對象進行相等性的比較,當值對象不能表達描述后,應該用另一個值對象替換
      • 無副作用:它不會對協作對象造成副作用,它有函數,只產生輸出,不改變誰的狀態

        Domain Primitive:我們在討論值對象完整性的時候,我們還會討論DP(Domain Primitive)領域基本值對象,它是值對象在領域層的升級版,具有不可破壞性,函數式編程給這種完整性提供了可靠的保障。它要求值對象無任何辦法被外界破壞其領域概念,記住完備的Value Object是不可破壞的。

        函數式編程(狀態所在的位置修改狀態):我們知道值對象可以封裝函數方法,這個時候如果值對象的入參是實體的時候,我們不能改變實體的狀態,而是返回結果讓實體自己去改變自己的狀態。

        最小化集成:在集成界限上下文的時候,我們盡量用值對象集成,使得上下文之間只需了解對方最小的信息量,做到最小集成。

        標準類型:可以用值對象表示標準類型,枚舉是一個很好的實踐。

      • 一個共享的不變值對象可以從持久化存儲中獲取,此時可以通過標準類型的領域服務或者工廠來獲取值對象
      • 我們可以用聚合來表示標準類型,但是如果要被其他上下文交互,那么不變性需要我們權衡是否可以建模為值對象。

      領域服務:

        客戶方:應用服務是領域實體的客戶方,同時也是領域服務的客戶方。

      • 如何區分應用服務和領域服務,主要看是否是業務邏輯和計算機邏輯。
      • 通用語音指導:一段邏輯是封裝到實體還是封裝在領域服務中,主要參考通用語言及復用性,職責性。
      • 抽象不同,場景不同,一個邏輯是屬于領域服務還是實體都有可能,可以通過重構感覺他們的權衡利弊。

        可能場景:如下場景可用:

      • 執行一個顯著的業務操作過程
      • 對領域對象進行轉換
      • 以多個領域對象作為輸入進行計算,結果產出一個值對象

        獨立接口:我們喜歡寫一個業務服務,給這個業務服務定義一個接口,其實沒有必要

      • 但不管如何,如果有接口,我們可以把領域服務的接口/實現和實體放在一個模塊中。用作提現通用語言
      • 如果有接口,是一個基礎設施的實現,那么我們可以把實現倒置出去
      • 當你使用加上impl對一個領域服務接口進行實現的時候(非倒置情況),通常來說,這表明你不需要一個接口,其實不同實現應該有不同命名,impl代表只有一個實現

        通用語言(感悟):領域服務,其實是一種特殊的實體對象。你完全可以把當作一個實體來對待

      領域事件:

        領域事件是領域模型的組成部分,表示領域中所發生的事情,是通用語言的正式組成部分。使用領域事件我們可以講任何企業系統設計成自治服務和系統。

        跟蹤實體狀態變化:大部分情況我們沒必要對整個生命周期變化進行跟蹤,如何跟蹤實體的對象變化?最實用的方法就是存儲領域事件和事件存儲

        維護事件的一致性:我們希望實現聚合原則,即單個事務,只允許對一個聚合實例進行修改,由此產生的其他修改必須在單獨的事件中進行。

        通用語言:在建模領域事件的時候,我們應該根據界限上下文中的通用語言來命名事件及屬性。

        訂閱方:可以分輕量級訂閱方和重量級,后者會存儲事件,前者即時轉發,或者直接消費

        具有聚合特征的領域事件

      • 有些領域事件不是聚合產生的,可能是某些請求產生的,這個時候可以讓領域事件具有聚合特征(即事件=聚合)。
      • 這種事件可以被資源庫存儲,也必須要有唯一身份標識。
        • 因為不同上下文的消費方需要知道一個消息是不是被重復發送,相同標識的事件要冪等
      • 這種事件無法被刪除

        從領域模式中發布領域事件

        為了避免暴露模型,一種簡單有效的方式就是使用觀察者模式。另外我們可以配合EventBus(或者叫DomainEventPublisher)使用觀察者模式(也可叫發布訂閱模式吧,雖然他們有一些差距),下面討論一下EventBus的兩種使用方式。

      • 調度者發布模式:使用應用層ApplicationService來動態設置領域事件的訂閱者,并維持在ThreadLocal上,這里會引入調度著的概念。
      • 主題對象發布模式:使用對象來維護訂閱者,即對象被誰觀察是過程概念,而并非調度概念。所以是用領域對象本身來持有訂閱者,組裝的過程
      • 調度實例化發布模式:依舊按照調度者發布模式,但是維護關系不在主題對象上,而是用專屬的對象管理訂閱關系,把調度者實例化。

        向遠程的界限上下文發布事件:這里討論的是遠程,所以要借助基礎設施中間件,有以下幾種方式:

      • 領域模式和消息設施共享持久化存儲,優點是性能高;
      • 使用XA事務控制,兩階段提交,具體例如入MetaQ支持事務消息;
      • 把領域事件存儲在和聚合一樣的數據庫,這個和1很像;

        事件存儲的使用特征

      • 講事件存儲作為一個消息隊列來使用
      • 可以作為所有命令方法的流水記錄,用作debug等
      • 使用事件存儲中的數據來進行業務預測和分析
      • 使用事件重建聚合
      • 撤銷對聚合的操作

      聚合:

        原則1:在一致性邊界之內建模真正的不變條件(推理為何聚合原則是一個事務只修改一個聚合

        我們要建立一個聚合,一定要找到一種不變條件,這個條件表示一個業務規則,這種規則總是保持一致的,存在多種類型的一致性,其中之一是事務一致性。而我們通常用數據庫來保持事務的ACID特性,單個事務中,我們只修改一個聚合實例,這樣做很難,但是是值得的。同時這也是為什么我們使用聚合的原因。

        原則2:盡量設計小聚合

        考慮系統性能和可伸縮性,大聚合是很痛苦的。同時也有助于事務的沖突減少。另外我們也可以盡可能的在聚合內設計值對象。

        不要相信每一個用例:很容易出現一個問題就是,一個用例中會修改多個聚合,這個情況下,我們要搞清楚,對用戶需求的實現是否分散在多個事務中,還是單個事務,如果是后者就注意了,可能這些用例不能真正的反映模型中的真正的聚合(或許這個時候需要重構我們的聚合,產生新聚合出來)。注意很多情況下,我們其實是可以通過最終一致性修改多個聚合的。

        原則3:通用唯一標識引用其他聚合

        通過以上看出,不同聚合根之間不應該直接引用另一個聚合根。所以聚合根之間的引用,應該通過唯一標識進行。

        建模對象的導航性:既然聚合之間沒有直接引用,我們需要其他手段建立他們直接的導航性,應用服務是一個好的選擇,領域服務也可以。

        可伸縮性和分布式:因為采用了小聚合和標識引用,所以我們可以在一個核心領域下的不同界限上下文上應用分布式服務,并從中得到好處。通過領域事件,標識引用形成了遠程聚合之間的合作者關系。

        原則4:在邊界之外使用最終一致性

        其實最終一致性的時延,領域專家基本是可以接受的,想想沒有計算機的年代是怎么處理的就理解了。

        Eric Evans,問問我們是否應該由執行該用例的用戶來保持數據的一致性。如果是,請使用事務一致性,當然此時依然需要遵循其他聚合原則。如果需要其他用戶或者系統來保證數據的一致性,請使用最終一致性。

        打破原則的理由

      • 方便用戶界面,例如批量操作
      • 缺乏技術機制,就是沒有消息中間件
      • 全局事務
      • 查詢性能,一個聚合引用其他聚合有利于性能提升。

      工廠:

        領域工廠:工廠方法,工廠模式,通常指的是一個具有單一職責的方法,這個方法的唯一職責就是創建對象,而創建聚合對象也是其中一種對象。

      • 因此,我們并不關心一個工廠方法到底存在于哪里,如果單獨用一個類包裝,那么稱之為簡單工廠模式
      • 如果用一個接口表示該方法,把方法的創建開放出去,那么稱之為抽象工廠模式。

        領域對象,如果模型也有方法是專門用來生產對象的,那么也稱之為工廠方法。

      資源庫:

        內存無差異:資源庫是管理中間狀態的一種抽象組件,他讓用例過程感覺,聚合根似乎一直存在于內存中一樣。

        和聚合的關系:通常來說,聚合類型和資源庫存在一對一的關系。

        面向集合的資源庫:面向集合的資源庫,一個例子就是用一個Map來實現資源庫

      • 資源庫一直持有聚合引用,所以需要隨時隨地跟著聚合的變化。
      • 不能向資源庫添加多次聚合根,而且我們不需要重新保存聚合到資源庫。
      • 隱式讀時復制/隱式寫時復制,是資源庫跟蹤聚合根的兩種實現。

        面向持久化的資源庫:其實就是一種實體轉移模式

      • 適合用在面向聚合的數據庫
      • 每次事務結束,就把聚合實體整個放回資源庫,做全量的覆蓋,所以沒有更新這個說法。

      聚合與事件源(A+ES):

        事件源通過事件來表示一個聚合的完整狀態。這里的事件源自創建聚合的一系列事件。這些事件流應該按順序存儲起來,并且以聚合根標識隔離開來。

      • A+ES能保證聚合每一次變更的原因都不會丟失
      • 事件追加具有很高的性能,而且支持不同數據復制的方案
      • 以事件為中心使得開發者更加注重通用語言

        缺點

      • A+ES需要我們對業務由很深的了解,并且只有很復雜的實體才值得這樣做
      • 缺少工具和一致的知識體系,具有較高的風險
      • DDD本來就不多,有經驗的開發者更不多
      • 需要配合CQRS,這個時候會增加學習成本

        一般步驟

      1. 客戶端調用應用服務中的某個方法
      2. 獲取所需的領域服務以執行業務操作
      3. 根據客戶端傳入的聚合根id加載事件流
      4. 通過事件流重建聚合
      5. 向聚合傳入參數和領域服務執行
      6. 聚合可能雙分排領域服務或者其他聚合執行業務,然后產生新的領域事件作為輸出
      7. 新的領域事件用作改變聚合狀態,以及把聚合變更放入事件流
      8. 將新追加的事件通過消息設施發送給訂閱方

        如下圖所示,Mutate方法是聚合用來接受事件的,它將會根據事件的類型,選擇相應的方法去恢復實體狀態,這個對應的是第4步:重建聚合。

       

        業務操作是如何執行的?在聚合重建后,應用服務會把處理邏輯委派給聚合實例的一個命令方法。該聚合實例將使用其當前狀態及領域服務來執行業務操作。隨著行為的執行,對聚合狀態的修改將通過新的事件予以記錄,之后每個新的事件都會傳給聚合的Apply()方法。下面看下Apply的用法

        對應代碼:(其實和狀態管理模型很像)

      public partial class Customer{
        ...
        void Apply (IEvent event){
        //將事件追加到事件列表中,之后持久化
        Changes.Add(event);
        //傳入每個事件以修改當前內存狀態
        Mutate(event);
        }
       ... }

        命令處理器

        

       

      posted @ 2022-05-15 15:32  飯小胖  閱讀(617)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 亚洲中文久久久精品无码| 色综合久久综合久鬼色88| 色狠狠色噜噜AV一区| 无码人妻精品一区二| 亚洲日本中文字幕天天更新| 中文字幕国产日韩精品| 巨胸喷奶水视频www免费网站 | 亚洲高清日韩专区精品| 亚洲欧美综合人成在线| 国产中年熟女高潮大集合| 色偷一区国产精品| 97成人碰碰久久人人超级碰oo| 永宁县| 亚洲综合一区二区三区| 精品一区二区久久久久久久网站| 中文字幕在线观看一区二区| 亚洲开心婷婷中文字幕| 中国亚州女人69内射少妇| 十八禁日本一区二区三区| 奉化市| 成A人片亚洲日本久久| 她也色tayese在线视频 | 久久久久久久久毛片精品| 国产成人精品无码专区| 成人免费无码av| 欧美成人无码a区视频在线观看| 国产成人av三级在线观看| 国产一区二区三区日韩精品| 久久精品视频这里有精品| 国产日韩av二区三区| 少妇激情av一区二区三区| 精品无码久久久久国产电影| 一区二区三区激情免费视频| 国产一国产看免费高清片| 大又大又粗又硬又爽少妇毛片| 日本a在线播放| 亚洲一区二区三区啪啪| 中文无码热在线视频| 欧美色欧美亚洲高清在线观看| 日韩区中文字幕在线观看| 久9re热视频这里只有精品免费|