使用DDD聚合發現隱藏的業務規則的案例分析:資料庫事務的業務實現 - Nick Tune
在現實世界中,我們可能會對我們的業務規則和流程含糊不清。我們可以設定例外,也可以繞過一些步驟以適應我們從未想到的特殊情況。
想象一下一個業務規則,即所有客戶都必須具有名字,中間名和姓氏。如果某人訪問實體商店時沒有中間名甚至沒有姓氏的,則可以寫下他們的名字。
在軟體中,無法實時應對意外情況。我們必須在程式碼中非常精確地指定我們的業務規則是什麼,計算機將完全按照我們的指示來應用它們。
將模糊的現實世界業務規則轉換為精確的計算機程式碼時,可能會出現一系列問題,從而極大地影響客戶體驗,創收甚至公司聲譽。
如果某個系統無法為來自不同文化,具有不同命名約定的人提供服務,則這不是很好的生意或宣傳。
為了避免此類問題,需要最小化定義業務需求與將它們轉換為軟體之間的差距。這是組織應採用領域驅動的思維方式的地方。技術專家和業務專家共同協作進行領域建模,以識別實際的業務規則。
領域驅動設計的聚合模式用於彌合差距。它充當協作工具,可以平衡對業務正確性,使用者體驗和技術效率的需求。
本文中使用的醫療保健示例基於我最近與Kacper Gunia進行的基於實際客戶需求的討論。這不是一個虛假的例子,它是我們與客戶合作時面臨的典型挑戰。
事務和不變業務規則
為了在軟體系統中實施業務規則,需要確定事務邊界:哪些業務規則必須一起成功或失敗。這在業務和技術層面都具有重大意義。
例如,使用者必須在建立帳戶之前指定其名字,中間名和姓氏。如果沒有這三個名稱,他們將無法建立帳戶。當使用者輸入詳細資訊時,如果他們不滿足所有條件,則他們建立帳戶的請求(即交易)將被拒絕。
業務規則是,在任何情況下,沒有名字,中間名和姓氏的客戶都不應存在於系統中。我們將此稱為不變業務規則。
定義聚合邊界
確定特定的業務運營應該成功還是失敗聽起來像是一個簡單的需求收集練習。當您在職業生涯中獲得經驗時,您會意識到並非如此。
考慮用於醫療保健實踐的預約計劃系統的場景。每天都有60個時段,每個時段持續10分鐘。在軟體系統中,我們需要確定業務事務邊界(又稱為DDD聚合)。
開發人員決定將一個10分鐘的時間段作為其總邊界。他們選擇它是因為它較小,並且應該更快地載入和儲存在資料庫中。
一切似乎都很好,直到開發人員開始收到一些患者在同一天竟然預約多次的投訴,但是業務規則是不允許這樣做的。
資料庫事務的限制
開發人員嘗試在程式碼中新增一條規則:即無論何時患者請求預約,如果他們在同一天已經進行了預約,則第二次預約將被拒絕。
但是有一個問題:資料庫事務。如果患者嘗試幾乎同時預訂兩個預約,則可以跳過該規則,並且將確實可以進行同時兩個預訂成功。這是在新增防止重複預訂的業務規則之後還會發生,怎麼辦呢?
患者是可能會預訂多個預約,因為資料庫事務與聚合事務不符,兩者邊界不對齊,因為聚合邊界是一個10分鐘時間段,資料庫需要兩個事務來更新兩個時間段。如果第二個事務在第一個事務之後但在第一個事務完成之前開始,它將不會知道第一個預約已安排好,它將儲存第二個事務。(banq注:患者預約和醫院安排確認預約是流程中的兩個步驟,今天患者預約,明天醫院才能安排確認,這是一個長時間的事務過程。)
增加聚合大小以增強業務正確性
您可能已經注意到,這裡有一個簡單的解決方案:使聚合邊界更大,而不是一個10分鐘的時間段,聚合可以是以日程來安排:一天中的所有時間段,聚合的邊界不是10分鐘時間段,而是“一天”。
如果患者現在嘗試在同一天預訂2個預約,則資料庫事務將阻止安排第二個預約。如果尚不清楚,請盡力瞭解資料庫ACID工作原理…
第一個事務將開始第一次預約,然後第二個事務將開始第二次預約。第一個事務將提交併儲存第一個預約。但是,當第二筆事務試圖儲存第二筆預約時,它將失敗。
第二個事務失敗,是因為資料庫可以看到對第一個事務內的聚合進行了更改,並且如果不覆蓋第一個事務中的資料,則提交第二個事務可能並不安全。這就是所謂的樂觀併發。
看來問題已解決。病人已經不能在同一天安排兩次預約了。
併發衝突影響使用者體驗
通過增加聚合邊界的大小,帶來的意外副作用的風險也會增加。以下示例說明了這一點。
副作用之一是引入了新的“ 不變業務規則”,該規則規定,如果患者的預約被診所取消安排,則應自動將其安排為另一次預約。如果預約沒有進入安排,則任何患者都不得取消預約(banq注:這裡預約和安排確認是一個業務流程中兩個環節,患者預約了醫院不一定安排確認,但是醫院安排確認了的預約,患者就不能取消)。
當前的聚合將不允許強制執行這種不變性,不變性規則是跨數天,而聚合的邊界是單日內。(banq注:這種不變性其實是流程的不變性,是跨天長時間的,今天預約,明天才被安排確認)
因此,又再次提出簡單的解決方案:聚合現在可以是按周Week Schedule或按月Month Schedule。
實際上,為什麼不將聚合設計為按年度“ Year Schedule”為邊界呢,因為“沒有患者在一年內可以有20個以上的預約”也是一條業務規則啊。
答案是效能,但更重要的是併發衝突。這不是技術問題,這是損害使用者體驗的業務問題。
如果聚合為“ Week Schedule”,則也可能會影響兩位試圖在同一周預訂不同時段的患者。第二位患者的請求將被拒絕,因為聚合已經更新。使聚合更大會使問題更加嚴重。
業務中沒有人希望阻止兩名完全分開的患者在不同的日期進行預訂。但是業務專家不懂軟體。只是看到那些不稱職的開發人員在為技術而煩惱。
發現真實的業務規則
對於初級或幼稚的開發人員而言,聚合問題似乎是技術問題。但是解決這些問題的方法實際上只能是更好地瞭解領域。
瞭解執行不變業務規則對效能和併發性的影響後,我們可以問業務,如果患者在同一天預訂兩次預約或未立即提供重新安排會發生什麼情況?
如果我們取消了患者的預約,並且又花了10分鐘才重新安排了新的預約,該怎麼辦?
飛機會從天上掉下來嗎?醫療用品會用完嗎?世界海洋會枯竭嗎?
從軟體競爭條件到最終的業務規則
是通過拒絕患者預約使使用者煩惱?還是推遲10分鐘重新安排患者的預約,企業很可能會發現:不變性規則並不是真正的不變性,而只是最終不變的業務規則(有時開發人員會假設業務規則是不變的)。
確定放置聚合邊界的位置直接決定系統中可能發生的競爭條件的型別。通過探索聚合邊界並明確業務規則是真正的不變還是最終不變,對於找到良好的聚合邊界絕對必要。
如果您是開發人員,並且不打算與領域專家一起探索聚合界限,那麼您可能會遇到次優界限(banq注:不解決根本問題的修修補補的邊界劃定)。
選擇聚合邊界的公式
從根本上來說,選擇聚合邊界是一個簡單的方程式,在此我們可以權衡以下條件:
- 正確性:執行永遠不應該違反的業務規則
- 併發性:確保使用者可以並行工作而不會互相影響
- 複雜性:大型的複雜聚合或非同步流程(對於最終業務規則通常是必需的)可能會增加維護成本和軟體應用程式的可靠性
- 效能:無需載入和儲存資料庫中的大資料有效負載,從而優化了系統的響應能力
找到正確的平衡是挑戰所在,這是一項技能。
也許聚合較大會更好:醫療保健機構可能只有一名醫生,每小時僅需要1或2個約會請求。效能和併發性無關緊要。因此,正確性和簡單性是可取的。
也許小聚合比較好:每天有100位醫療保健從業人員,而一組呼叫中心的工作人員則在不斷進行預訂。效能和併發性至關重要,因此必須犧牲正確性和簡單性。
也許介於兩者之間比較好:有20位醫療保健從業人員和一些呼叫中心人員。定期制定“ 年度時間表”會導致併發衝突,因此太大了。但是,具有“日程安排”將需要分配更復雜的重新安排工作流程。
通過與企業討論,您可能會了解到所有重新安排的99%僅影響單個日曆周。如果通過將“ 周計劃”定義為聚合大小仍然很好並且沒有併發問題,那麼這似乎是滿足要求的不錯選擇。
也許有一個真正不變業務規則:他們真的存在嗎?
隨著時間的推移不斷髮展聚合
聚合設計所依據的初始標準可能會隨時間而變化。因此,您必須不斷評估和挑戰您的聚合界限。
如果醫療保健機構僱用了更多的醫療從業人員和呼叫中心人員,則突然的效能和併發性問題可能會成為真正的問題(這確實確實發生了)。
在這種情況下,平衡從需要高正確性和低簡單性轉變為需要高效能和併發性,因此應重新設計聚合。
瞭解您的領域以查詢良好的聚合體
希望現在很清楚,聚合的DDD概念不僅僅是軟體開發人員所追求的技術模式。聚合鼓勵我們深入研究該領域並提供更好的使用者體驗。
那麼,如何找到一個好的聚合呢?
- 為您的領域建模以發現潛在的不變性
- 用“假設”問題挑戰不變數,以確定補償動作是否足夠
- 分析任何現有資料,以確定聚合對效能和併發的影響
- 建立正確性,併發性和效能的最低階別
- 確定滿足最低要求級別和最低複雜性級別的基本設計
- 探索增加複雜性但提供業務收益的替代模型
- 預測未來趨勢
- 建立儀表板以視覺化效能和併發級別
- 永不停止探索更深的領域見解
領域驅動設計提供了一套以幫助您實現:EventStorming,領域講故事和更多。
(banq注:患者預約和醫院確認安排預約是一個流程的兩個步驟,如果患者的預約被診所取消安排,則應自動將其安排為另一次預約;如果預約沒有被醫院安排,則任何患者都不得取消預約,這是一個流程事務過程,聚合只能設計作為流程中一個步驟,無法涵括整個流程,否則就變得太大,流程的長時間事務必須通過業務流程自身來設計,而不是一味尋求資料庫事務或聚合來完成,但是通過聚合設計,可以發現時間維度上隱藏的概念,天是時間段的集合聚合,周是天的集合聚合,月是天的集合聚合,具體以哪個聚合大小設計,需要結合業務特點和流程,不要試圖用聚合做流程的事情,也不要讓聚合做基本時間段內微觀的事情)
相關文章
- DDD中實現業務規則的驗證 - Marcin
- DDD 中的那些模式 — 使用 Specification 管理業務規則模式
- 使用 Drools 規則引擎實現業務邏輯
- 用SQL Server觸發器實現業務規則的強制執行SQLServer觸發器
- 讓業務實現迴歸資料庫資料庫
- 使用業務能力方法實現DDD戰略建模 - pulse
- MySQL資料庫分散式事務XA的實現原理分析MySql資料庫分散式
- 面對善變的需求,業務專家的特點是善於發現業務規則 - RomainTrmAI
- 根據業務能力實現DDD建模 - trond
- 資料庫分散式事務的實現原理!資料庫分散式
- 可以促進微服務設計的DDD事件風暴建模技巧 - Nick Tune微服務事件
- 通過語言的比喻句發現隱藏的DDD模型 - verraes模型
- MySQL資料庫事務隔離性的實現MySql資料庫
- 便捷的業務規則桌面工具
- 使用Spring Data JDBC實現DDD聚合SpringJDBC
- 決策表模式: 一種業務規則引擎實現方式模式
- 使用Spring Data JPA實現DDD聚合的動態投影Spring
- 快速實現業務規則的開源API邏輯伺服器簡介API伺服器
- 大資料 - DWS層 業務實現大資料
- Drools 業務規則引擎的完整教程
- 使用Spring Cloud Gateway隱藏執行時的服務發現SpringCloudGateway
- 如何使用SAP的後設資料框架 (MDF) 構建自定義業務規則?框架
- 使用CRDT實現分散式事務的資料推薦分散式
- [TEAP早期試讀]在資料庫中使用PL/SQL實現業務邏輯的優勢資料庫SQL
- 資料管理:業務資料清洗,落地實現方案
- 為何打通業務資料,實現資料流通?
- 業務流程的新實現:微服務和事件編排微服務事件
- 分散式事務之資料庫事務與JDBC事務實現(一)分散式資料庫JDBC
- 使用Go的Defer和Rust的Drop實現資料庫事務機制的比較 - DEVGoRust資料庫dev
- CSS實現隱藏超出的內容CSS
- C++“隱藏實現,開放介面”的實現方案C++
- 用規則引擎開發靈活配置的業務系統
- 挖掘資料隱含的業務資訊(1)——訂單銷售交易類資料分析
- 使用 WebSphere Business Modeler 實現業務流程的直接部署Web
- Spring Boot實現DDD的貨運Cargo微服務案例原始碼Spring BootCargo微服務原始碼
- 一則資料庫無法重啟的案例分析資料庫
- 理解資料庫連線池和ThreadLocal實現的事務控制資料庫thread
- 為 Key-Value 資料庫實現MVCC 事務資料庫MVC