2015-03-02 20 views
0

我想問一個我在遊戲實現中使用DDD的問題。如何在同一事務中創建ddd聚合並更新其引用的其他聚合?

我有一種情況,根據系統收到的消息,創建一個新的玩家聚合。問題來自於這個新聚合必須由另一個聚合(表聚合)使用其id來引用。這第二個聚合保留了一個值對象,其中包含關於表中玩家的信息,如他所選擇的位置或顏色。玩家聚合存儲玩家動作,因爲根據玩家數量和他們發佈的動作,表聚合可能變得很龐大,如果我把所有東西放在桌面上,數據庫中的鎖爭用代價太高。對於玩家能夠加入表格,必須首先傳遞一些驗證,例如未採用的顏色,已達到的最大玩家數量或玩家不活躍的遊戲,這就是爲什麼此值對象存儲在信息表格中的原因來自球員。

因此,基於此,我只在創建玩家聚合時發佈第一個要存儲在表外的動作。問題在於該表可能包含引用尚未創建的聚合的值對象,這會使代碼看起來很難看,因爲它會強制它檢查空引用。另一個選項,即首先創建玩家聚合,也會破壞設計,因爲創建玩家的驗證首先發生在桌子上。由於系統的併發特性,首先在表上進行驗證,然後創建玩家聚合,然後在從創建中接收事件時更新表,可能會導致很多競爭條件。我能想到的唯一事情就是在同一個事務中更新兩個聚合,但這違背了DDD規則。

有沒有什麼好的解決方案呢?我想最終這將與設計有關,但我想不出任何可以在不引入性能問題的情況下工作的方式。

謝謝。

+0

怎麼樣像'player = table.newPlayer(...); playerRepository.save(播放器); tableRepository.save(table);'其中'Table'只是將VO添加到內部集合中。在這裏你不會修改同一個事務中的兩個聚合,因爲玩家只是被創建,沒有被修改。因此,玩家實例上不存在爭用。 – plalx 2015-03-02 14:58:47

+0

你真的可以這樣做嗎?我認爲關於在同一交易中不修改兩個集合的全部內容也包括創建。即使這樣也不適用於我的情況,因爲玩家離開表格後不會從PlayerRepository中移除。我必須檢查,因爲我不知道我是否會破壞別的東西。還有一個要求是存儲玩家的快捷方式,但可能在另一個有界的上下文中,所以在他離開後移除玩家仍然可以安全。但我想知道,如果更新和創建在同一時間是有效的DDD。 – Kilian 2015-03-02 15:09:13

+1

是的,我相信它是有效的,因爲沒有增加併發衝突的風險。例如。您只修改'Table'聚合或修改'Table'聚合,並且創建一個'Player'對象並保存它並不會改變任何併發衝突。併發衝突是避免在單個事務中修改**許多聚合的主要原因。 – plalx 2015-03-02 17:42:16

回答

2

通常,部分問題可能通過從域和無處不在的語言角度查看它們來解決。

  • 烏迪大漢有excellent article有關如何聚合根目錄不憑空出現的,但作爲必須是一流的通用語言市民域作用的結果。在你的情況下,播放器可能不是由應用程序服務創建的,但是最接近域源,由Table AR創建。

    請注意,我並不主張持就像烏迪的例子,其他全根引用,但你可以替換成Table.ReceivePlayer(...)返回新創建的播放器,然後可以由應用軟件服務被添加到PlayerRepository。由於Player在該場景中不扮演聚合根(即修改和不變實施的單個入口點)的角色,而是扮演簡單實體的角色,因此您可以合法地將所有這些內容包裝在單個事務中。

  • 驅動聚合建模的力量基本上是真正的不變量,併發性和性能。爲了證明你如何建模你的模型,你聲明表的聚集可能會變得很大,如果我把所有的東西都放在表上,那麼在數據庫上的鎖爭用將會代價太大。

    我認爲這將有助於進一步探索這種爭論的真正含義,並將領域術語置於其後,從而豐富您的無處不在的語言。一個表是否大規模併發?怎麼會這樣 ?遊戲是否具有玩家修改的可變共享狀態?球員是否需要根據其他球員的動作做出決定,如果在同時採取同時採取的行動時引入某種偏見?所有這些在領域中的翻譯如何?額外的Player聚合帶給你什麼?

    就性能而言,龐大的Table聚合體會是什麼樣子?它會如何影響系統?

  • 「沒有跨越多個集合的事務」規則僅僅是將集合作爲一致性邊界的必然結果,它並不是一成不變的。在單個事務中創建小的,獨立一致的聚合並對其中的多個修改進行大量修改是沒有意義的,因爲您創建了不必要的爭用,並導致潛在的併發問題。但是在沒有併發問題的情況下,除了唯一的併發問題之外,創建的情況確實如此,因爲沒有其他人知道要添加播放器,這就好像您只修改了一個集合,而且規則實際上不再成立。

    如果您尊重99%修改案例中的規則,但不在1%創建案例中,則您仍然處於行列。

+1

關於無處不在的語言的問題,我已經多次思考過,並且系統可能會遭受性能問題的困擾,我認爲這些問題已被很好地識別出來。我可能沒有做的是考慮它的專有名稱以及它應該如何工作。 爭用來自投注不能全部存儲在同一個聚合中的事實。這樣做會導致一個巨大的表格,正如我所說的那樣,可能有數百人,玩家可能會同時投注。但是第二個聚合體應該反映的是這個存儲,所以它可以被稱爲PlayerBetsBook – Kilian 2015-03-05 15:33:11

+0

接下來,我認爲現在這個第二個聚合,這樣做,應該創建和刪除播放器。這將解決引用尚未創建的問題的問題,因爲這不會再發生。但是在同一個事務中創建它並不是一個選項。即使不打破任何DDD規則,我使用的MongoDB在處理事務性時並沒有幫助。所以我會堅持懶惰的創作,當Player離開解決我的設計問題時添加刪除。 感謝您的解釋,它非常有幫助。 – Kilian 2015-03-05 15:39:08

+0

不客氣。現在我明白爲什麼這第二個總結 - 爲什麼你應該總是在一個問題中儘可能地描述上下文:)完全說明你的實現接近「拆分熱聚合」策略:http://www.jefclaes .BE/2014/11 /分裂,熱aggregates.html。你有沒有任何「與桌面和解的PlayerBetsBook」操作發生在間隔時間內,就像在該博客文章中一樣? – guillaume31 2015-03-06 11:18:06