2013-07-20 81 views
6

我在閱讀弗農的文章Effective Aggregate Design。我有一個問題,爲什麼每個事務只修改一個聚合實例?爲什麼每個事務只修改一個聚合實例?

讓我們舉一個例子,考慮一個倉庫逆轉管理故事。

庫存代表一個物品在倉庫中的數量。例如上海倉庫的5 Implementing Domain Driven Design書。

條目代表關於庫存的輸入/輸出操作的日誌。例如,在上海倉庫中輸入2 Implementing Domain Driven Design書籍。

庫存的數量需要改變,如果條目提交。

它很容易出現在我的腦海裏,這是一個不變的可以通過事務一致性來實現。

溶液A:使用一個聚合和簇條目庫存

public class Inventory implements Aggregate<Inventory> { 
    private InventoryIdentity id; 
    private Sku sku; 
    private int quantity; 
    private List<Entry> entries; 

    public void add(Entry entry) { 
     this.quantity += entry.getQuantity(); 
     this.entries.add(entry); 
    } 
} 

public class Entry implements LocalEntity<Entry> { 
    private int quantity; 
    // some other attributes such as whenSubmitted 
} 

public class TransactionalInventoryAdminService impelments InventoryAdminService, ApplicationService { 

    @Override 
    @Transactional 
    public void handle(InventoryIdentity inventoryId, int entryQuantity, ...other entry attributes) 
     Inventory inventory = inventoryRepository.findBy(inventoryId); 
     Entry entry = inventory.newEntry(entryQuantity, ..); 
     inventory.add(entry); 
     inventoryRepository.store(inventory); 
    } 
} 

溶液B:使用單獨的骨料庫存進入

public class Inventory implements Aggregate<Inventory> { 
    private InventoryIdentity id; 
    private Sku sku; 
    private int quantity; 

    public void add(int quantity) { 
     this.quantity += quantity; 
    } 
} 

public class Entry implements LocalEntity<Entry> { 
    private Inventory inventory; 
    private int quantity; 
    private boolean handled = false; 
    // some other attributes such as whenSubmitted 

    public void handle() { 
     if (handled) { 
      throw ..... 
     } else { 
      this.inverntory.add(quantity); 
      this.handled = true; 
     } 
    }  
} 

public class TransactionalInventoryAdminService impelments InventoryAdminService, ApplicationService { 

    @Override 
    @Transactional 
    public void handle(InventoryIdentity inventoryId, int entryQuantity, ...other entry attributes) 
     Inventory inventory = inventoryRepository.findBy(inventoryId); 
     Entry entry = inventory.newEntry(entryQuantity, ..); 
     entry.handle(); 
     inventoryRepository.store(inventory); 
     entryRepository.store(entry); 
    } 
} 

A和B是可行的,但溶液B是離開無意oppertunity調用庫存。新增(數量),而不條目涉及種不雅。 這是什麼規則(每個事務只修改一個聚合實例)試圖指出我?我很困惑,爲什麼我們應該只修改一個交易中的一個集合,如果我們不這樣做,會出現什麼問題。

UPDATE1開始

難道打算減輕併發問題(與其他規則 「使小集合」)?例如,條目是具有相對低的爭用和庫存是一個具有相對高的contetion(假定多個用戶可以操縱一個庫存)集合體,它會導致如果我修改它們都在一個事務不必要併發故障。

UPDATE1結束

需要的,如果我採用溶液中的待解決一些其他問題:

1。如果有很多條目 s爲庫存,我需要一個分頁查詢用戶界面?如何使用集合實現分頁查詢?一種方法是加載所有條目 s並選擇頁面需要的內容,另一種方式是InventoryRepository.findEntriesBy(invoiceId,paging),但這似乎違反了獲取本地實體的規則,只能通過獲取它的aggreate然後導航對象圖。

2.什麼,如果有太多的進入 S代表的庫存,我有當添加一個新的進入加載所有的人?

我知道這些問題源於缺乏完全理解。所以任何想法都是值得歡迎的,事先要感謝。

+0

我認爲,這種做法也減輕可能發生死鎖http://stackoverflow.com/questions/735894/database-deadlocks –

回答

10

經驗法則是讓您的聚合小,因爲您希望避免由於併發導致的事務性失敗。我們爲什麼要讓內存佔用量大,如果它不應該?

所以,解決方案A並不是最優的。大集合經常引入容易避免的問題。

確實,另一個經驗法則是隻更改一個事務中的一個集合。如果您使自己的彙總進入,您可以使庫存的數量最終保持一致,這意味着入口彙總可能會引發庫存訂閱的事件。這樣你只能改變每個事務的一個聚合。

public class Entry { 
    public Entry(InventoryId inventoryId, int quantity) { 
     DomainEvents.Raise(new EntryAdded(inventoryId, quantity)) 
    } 
} 

如果你覺得不舒服最終一致性,你仍然可以保持獨立的聚集,但修改它們都在一個事務現在 - 直到你感覺疼痛,用封裝領域的服務。另一個選擇是保持域事件處理,以便它們也在單個事務中提交。

public class InventoryService { 
    public void AddEntryToInventory(Entry entry) { 
      // Modify Inventory quantity 
      // Add Entry 
    } 
} 
+0

+1謝謝你的回覆的機會。我很困惑,爲什麼我們應該只修改一個交易中的一個集合,如果我們不這樣做,會出現什麼問題。 – Hippoom

+0

你的提醒「經驗法則是保持你的聚合小」給了我一個線索,看到我的問題更新。 – Hippoom

+0

我不得不穀歌翻譯'減輕';)但是,爲了響應你的更新:是的。 – JefClaes

相關問題