38

我有一個領域模型,它具有編輯器和項目的概念。存儲庫模式:如何延遲加載?或者,我應該拆分這個聚合?

一位編輯擁有許多項目,而一個項目不僅有一位編輯所有者,還有一些編輯成員。因此,編輯也有一些「加入」項目。

我正在採取一種DDD方法來對此進行建模並使用Repository模式進行持久化。然而,我並沒有足夠好地確定我應該怎麼做。

我正在編輯和項目可能在相同的聚合,其中根是編輯器的假設工作。因此,我可以得到一位編輯,然後列舉其項目,並可以從那裏列舉項目的成員編輯。

但是,如果我只能從我的存儲庫中檢索編輯器,那麼這是否意味着當我獲取擁有它們的編輯器時,我不得不從存儲庫加載所有項目?如果我想延遲加載成員編輯器,項目也需要對存儲庫的引用?或者,如果我拆分聚合並且有一個編輯器存儲庫和一個項目存儲庫,那麼我應該如何處理這兩者之間的事務,比如何時將新項目添加到編輯器中?例如:

Editor e = new Editor("Editor Name"); 
editorRepository.Add(e); 

Project p = e.CreateProject("Project Name"); 
projectRepository.Add(p); // These two lines 
editorRepository.Save(e); // should be atomic 

我誤解了Repository模式的意圖嗎?

+1

你可能想看看我的相關問題:http://stackoverflow.com/q/20820302/253098 – SystematicFrank 2014-01-04 21:40:25

回答

30

我誤解了Repository模式的意圖嗎?

我會說「是啊」,但要知道,我和每個人我已經問了同樣的事情,出於同樣的原因曾... 「你不是想第四維,馬蒂」。

讓我們把它簡化一點,與建設者堅守而不是首先創建方法:

Editor e = new Editor("Editor Name"); 
e = editorRepository.Add(e); 

Project p = new Project("Project Name", e); 
p = projectRepository.Add(p); 

在下面,你的項目庫總是存儲有效所有者(p.EditorId)到項目的數據,因爲它的創建,但是,如果您重新編輯編輯器的項目,它將會在那裏。這就是爲什麼將所有必需的屬性放入構造函數是一個好習慣。如果你不希望傳遞整個對象,只是e.Id會做。

如果我想懶加載成員編輯器,項目還需要對存儲庫的引用?

現在,關於如何根據需求重新填充編輯器的項目,您有幾個選擇取決於您要做什麼。直存儲庫說,你想:

IEnumerable<Project> list = projectRepository.GetAllProjects() 
           .Where(x => x.editorId == e.Id); 

但是把它放在哪裏?不是在Project或Editor中,你是對的,或者他們將不得不訪問存儲庫,這是不好的。上面的代碼片段是鬆散耦合的,但不能自行重用。您剛剛達到了Repository Pattern的限制。

接下來是適用於您的應用程序的一個適配器層,帶有一個共享資源庫(StaticServiceWrapper)和某種類型的EditorAdapter對象(或Aggregate或任何您稱之爲的),或者現在您可以混合使用擴展方法可以流利地與任何和所有必需的存儲庫進行交流。我沒有做它到底這種方式在生產系統中,但向你展示一個簡明的例子:

public static class Aggregators 
{ 
    // one to one, easy 
    public static Editor GetOwner(this Project p) 
    { 
     return StaticServiceWrapper.editorRep.GetEditorById(p.editorId); 
    } 

    // one to many, medium 
    public static IEnumerable<Project> GetProjects(this Editor e) 
    { 
     return StaticServiceWrapper.projectRep.GetAllProjects() 
       .Where(x => x.editorId == e.Id); 
    } 

    // many to many, harder 
    public static IEnumerable<Editor> GetMembers(this Project p) 
    { 
     var list = StaticServiceWrapper.projectMemberMap.GetAllMemberMaps() 
         .Where(x => x.projectId == p.projectId); 

     foreach (var item in list) 
      yield return StaticServiceWrapper.editorRep.GetEditorById(item.editorId); 
    } 
} 

基本上,一旦你GETALL,GetById,添加,更新,刪除對象庫完成後,你已經有獨自離開了協會和移動上了對象/層層次的樂趣部件,如適配器和緩存和業務邏輯(「哦,我的天!」)。

+0

如果IRepository的底層實現是一個數據庫,並且你有一個非常大量的X實體。在存儲庫上使用GetAll()還是可以的,然後執行一個LINQ查詢整個集合?如果是太慢了,你寧願做查詢在DB級別是什麼,你會怎麼做,與存儲庫? – 2013-10-30 20:15:58

3

這取決於您的應用程序的需求。如果加載給定編輯器的所有項目是一個大問題,那麼嘗試使用延遲加載模式,例如Virtual Proxy

關於延遲加載項目的成員編輯,如果您使用虛擬代理,我沒有看到注入代理與編輯庫的問題,因爲我不認爲代理是域的一部分。

如果你拆分了Aggregate,你可以研究Unit of Work模式作爲原子性的一個解決方案。但是,這個問題並不是DDD所特有的,我相信還有其他的交易行爲解決方案。

4

如何將職責分解爲EditorOwner和EditorMember?

不知道你的域名,我想他們會有不同的責任 - 例如,EditorOwner可能相當豐富(可能是聚合根目錄),但是項目可能只需要知道有限的數量它的成員,所以EditorMember對象可能很輕。

這些域對象也可能與用戶有關,但這可能會在另一個上下文中。

這是否有助於解決問題,還是讓問題變得更加複雜?

0

在這裏,您有兩種不同的關係,一種是所有權,另一種是成員資格。

所有權關係很簡單(每個項目都有一個所有者)。會員關係是多對多的(許多編輯按項目編輯,許多編輯項目)。

您可以在Project類上提供Owner屬性,並在ProjectRepository上提供一個方法來獲取由特定編輯器擁有的所有項目。

對於一對多的關係,提供了項目類中的成員屬性,並在ProjectRepository的方法獲取包含指定的編輯器爲成員的所有項目。

看來還編輯和項目單位,我可能會分裂的總和,但也許這些條款在您的上下文中的具體含義,使其子實體的骨料。