DDD聚合五種設計方法

banq發表於2018-12-24
聚合是啥?聚合就是整體與部分的組合,這裡推薦一篇Szymon Kulec英文文件,點選標題進入後可獲得實現聚合的五種規則,該文件大意翻譯如下:

我第一次閱讀領域驅動設計(DDD)的藍皮書時,它改變了我對業務領域的看法。一開始,我認為這種新方法純粹是技術性的。我嘗試按照規定應用建模,以“DDD”或“非-DDD”的黑白態度處理每個解決方案,就像之間沒有其他任何東西一樣。在接下來的幾年裡,這種方法變得越來越模糊。畢竟,DDD就像任何其他方法/範例一樣,你可以選擇你想要使用的工具並從中獲得一些東西。即使您不使用DDD(再次,沒有黑白解決方案),有用的工具之一就是使用基於聚合的思維對您的解決方案進行建模。
我認為基於聚合思維的術語是什麼意思?我指的是使用建模技術使您的解決方案的某些部分獨立,可能會在以後的微/奈米或任何大小的服務中嵌入。只需看看技術的景觀及其不同的部分,例如公共雲或現代資料庫。即使不使用“完全DDD”,如果以正確的方式繪製原子部件的邊界,您的解決方案仍將獲得更多。而且,正確的方法是什麼?是否有任何規則要遵循?我鼓勵你繼續閱讀並學習其中的一些內容。現在是時候訪問你不想 破壞的5個聚合規則。

1.聚合是事務邊界
每當您發現域的兩個元素緊密結合在一起時,您就會發現潛在的聚合。根據使用的方法,聚合可以儲存為:

  • 文件,如果您使用文件資料庫
  • 實體ER圖,如果使用某種型別的ORM儲存聚合或者手動編寫SQL 
  •  事件流,如果使用事件源來捕獲聚合歷史記錄的所有業務更改

無論使用何種儲存機制,它們都共享一個共同的屬性。此屬性是包裝為儲存聚合狀態而執行的儲存操作的事務。是的,我故意使用相同的術語“交易”來描述:業務操作和儲存操作,因為它們真的是一樣的。無論您使用哪種儲存機制,一起操作的東西都將儲存在一起。當然,我們可以討論儲存實現的優缺點,但這並沒有改變事務性的事實。

找到必須一起更改的專案,您將找到聚合的一部分。

2.把聚合設計得大一些
在設計聚合時可以犯的一個常見錯誤是將所有內容切割成儘可能小的部分。您可以想象每個聚合只儲存一個值是什麼樣?如果分開的部分之間存在某種相互作用,您將需要以某種方式處理它。“只是呼叫聚合的方法”等常用方法將不再有效,因此將使用事件投影、流程管理器和Saga來處理這兩個或更多步驟流程。
每次將聚合體分成較小的部分時,請問自己是否在切割部件之間的某些重要關係。
比如在建模時,Peter發現使用者每次在聚合帳戶結算上執行操作時,都需要使用另一個名為Balance的聚合。在將它們合併到模型中之後,使用屬於這個新的單個聚合的資料,在沒有額外非同步通訊的情況下就可執行所有操作。

3.使聚合儘可能小
閱讀上一部分後,人們可能會認為製作大型聚合總是一個好主意。我經常使用的一個反例是建立一個名為The System,The Service或The Module的超級聚合。
由於聚合需要以原子方式更新,因此您有兩種處理事務的方法:樂觀和悲觀併發。
使用樂觀併發意味著嘗試更新/附加聚合資料,檢查版本是否在後臺不會更改。很容易想象,即使有兩個使用者在The System中積極工作,也很容易導致這些版本不匹配。
使用悲觀併發意味著無論何時想要對聚合執行操作,都需要以某種方式鎖定它。鎖定聚合通常會保留其邊界。這意味著,使用的鎖是粗粒度鎖,它將聚合作為一個整體鎖定。
在樂觀併發/鎖定的情況下,機制有點不同。從儲存中獲取聚合時,也會獲得其版本。一旦操作在記憶體中執行並且狀態更新,它將有條件地更新,並附加一個子句檢查以確保版本沒有更改。如果是,則發生錯誤並且應該嘗試執行操作。
在兩種併發情況下,只有一個actor可以一次執行一個操作。這意味著,有更細粒度的聚合,您的系統將能夠執行的操作越多,反過來,吞吐量就越高。
如你所見,有兩種相反的力量。一種是嘗試使聚合物儘可能大,另一種 - 使它們儘可能小。由您和您使用的模型決定如何拆分實體以及如何選擇聚合邊界。

4. 儘可能使用time-bounded時間維度範圍內的聚合
每當您為與時間緊密相關的領域建模時,尋找有時間範圍限制的聚合!。例如:建模“醫生預約”,有時間表或周計劃。你怎麼設計這種有時間限制的聚合?
首要的是為您的聚合找到合適的時間粒度。讓我們考慮醫生預約的例子。通常,約會不太可能需要一個多小時。另一方面,患者通常會在特定日期搜尋訪問並嘗試提前一天或一天​​後移動(至少這是我使用的演算法)。這種聚合的自然時間邊界將是一天。考慮到這一點,您可以將特定醫生的每個工作日建模為單獨的聚合,例如:對於ID為doc123的醫生,您將建立一個ID為doc123-2019-Jan-02的聚合,以處理1月2日的所有訪問。

5.翻轉主謂賓句子直至做對了
讓我們考慮以下句子:使用者正在訂購一本書。你想到的第一個元素是什麼?
模型能夠支援這種操作。是使用者嗎?以下句子怎麼樣:一本書是由使用者訂購的;還有這一個:使用者下了一本書的訂單。(banq注:主語更換了),最後一個是相當正常的,但是所有三個都描述了相同的操作,然而,表現出不同的語言表達。讓我們討論不同的模型以及它們可以代表什麼。

在第一個示例中,使用者正在訂購書籍,可以模擬表示使用者的單個聚合。這似乎很自然,因為它匹配執行訂單的自然人。這個聚合體的大小怎麼樣?不是太大了嗎?如果使用者購買了數千本書怎麼辦?我們真的想將這些資訊整合在一起嗎?以下操作是否取決於訂購歷史記錄?我會說,這不太可能。您可能希望不時顯示歷史記錄,但沒有操作需要整個歷史記錄來驗證是否可以執行該歷史記錄。

在第二個例子中,一本書是由使用者訂購的,人們可以想到一個書籍聚合,其中包含訂購了這本書的所有使用者的所有訂單。我們真的需要這種歷史嗎?當然有人可能會爭辯說我們需要保留我們擁有的副本數量,但通常情況並非如此。今天,電子商店出售商品,即使他們現在沒有庫存。因此,不需要在一個巨大的聚合中聚合所有內容。

對於最後一個示例,使用者下了一本書的訂單。現在,所有與同一訂單相關的資訊都放在一個地方。聚合很小,併為每個訂單建立一個新的聚合!根據需要新增使用者資訊和訂單行(banq注:這個合適!)

案例:Fumika正在嘗試提煉模型,在得出結論之前,她嘗試使用描述需求的翻轉主謂賓的句子來找到符合系統要求的最佳邊界。花了一些時間後,她發現了一個合理大小的聚合時間限制(因為她處理某種排程系統)。

 

相關文章