2015-06-03 94 views
1

考慮以下情形:DDD邏輯和持久性無知

public class Document 
{ 
    private ISet<User> sharedWith; 

    public Document(string name) 
    { 
    this.sharedWith = new HashSet<User>(); 
    this.Name = name; 
    } 

    public string Name { get; private set; } 

    public IEnumerable<User> SharedWith 
    { 
    get 
    {  
     return this.sharedWith; 
    } 
    } 

    public void ShareWith(User user) 
    { 
    if (this.SharedWith.Contains(user)) 
    {  
     throw new Exception("This document is already shared with that user."); 
    } 

    this.sharedWith.Add(user); 
    } 
} 
  • Documents可以與User
  • 當與用戶共享文檔,如果文檔已經被與該用戶共享共享,拋出異常。
  • 文檔可以與成千上萬的用戶共享。

顯然,這並不規模非常好,因爲需要檢查SharedWith用戶的存在,導致了ORM懶加載整個收集到內存中。我可能做應用程序服務中的存在檢查,但我認爲這個領域的邏輯,所以它對我最有意義,我把它保留在Document類。

我似乎無法弄清楚這應該如何與DDD完成?如果我無法使用ORM,那麼如何做這種事情呢?

我想我應該有一個文檔彙總和一個用戶彙總

我已經看過各種DDD資源(雖然我還沒有讀過這本書),但我似乎無法找到這個特定場景的答案。

+0

這種類型的邏輯可以/應該域服務中的歸屬。只是另一種處理這種事情的策略模式。你的回購可以有一個簡單的方法返回一個布爾文是否已經與一個特定的用戶共享文檔 – Marco

+0

@Marco然後我沒有理解什麼樣的邏輯應該在域類本身?我所描述的不是所謂的域邏輯嗎? – Jeff

+0

集合強制執行的一致性邊界是它的不變量。你的商業用戶真的*說過這是一個商業規則,還是你自己想出的東西? – Marco

回答

2

這是快速完成了,所以它不是完美的,但你得到它的要點:

public class User { public Guid UserId { get; set; } } 

public class Document 
{ 
    public string Name { get; private set; } 

    private ICollection<User> sharedWith = new List<User>(); 

    private DateTime? publishedOn; 

    public Document(string name) 
    { 
     if (string.IsNullOrWhiteSpace(name)) 
     { 
      throw new ArgumentException("Name is required"); 
     } 

     this.Name = name; 
    } 

    public void Publish() 
    { 
     if (this.publishedOn.HasValue == false) 
     { 
      this.publishedOn = DateTime.UtcNow; 
     } 
    } 

    public void SharedWith(User user) 
    { 
     if (this.publishedOn.HasValue == false) 
     { 
      throw new InvalidOperationException("Document must be published for sharing is allowed."); 
     } 

     sharedWith.Add(user); 
    } 
} 

public interface IDocumentRepository 
{ 
    Document documentOfId(Guid id); 

    bool IsAlreadySharedWith(Guid documentId, Guid userId); 
} 

public interface IUseRepository 
{ 
    User userOfId(Guid id); 
} 

public class ShareDocumentService 
{ 
    private readonly IUseRepository userRepository; 
    private readonly IDocumentRepository documentRepository; 

    public void ShareWith(Guid userId, Guid documentId) 
    { 
     if (documentRepository.IsAlreadySharedWith(documentId, userId)) 
      throw new InvalidOperationException("Document has already been shared with user."); 

     User user = userRepository.userOfId(userId); 

     Document doc = documentRepository.documentOfId(documentId); 

     doc.SharedWith(user); 
    } 
} 
+0

評論不適用於擴展討論;這個對話已經[轉移到聊天](http://chat.stackoverflow.com/rooms/79562/discussion-on-answer-by-marco-ddd-logic-and-persistence-ignorance)。 – Taryn

+0

這麼多改變,我也測試了一切,所以喜歡4倍多的工作! – Jeff

+0

既然我不再有實體之間的引用,我該如何將它映射到我的SQL數據庫中的關係?對不起,將其拖出去:P – Jeff

1

我想,如果你是在一個基於紙/演員的世界中建模的話,那麼有人會有編組誰可以訪問哪些文檔的工作,這可能會依賴某種基於紙張的人工製品。要訪問文檔,您必須填寫一份「文檔請求表單」,該表單可能需要經過審批流程。

該表格在紙質世界中將成爲多對多鏈接實體,成爲用戶訪問安全文檔的關鍵。這將使UserDocumentDocumentRequestForm三個獨立的實體。

+0

就我而言,它與問題中指定的一樣簡單。但是,我沒有看到如何將它分解爲「DocumentRequestForm」解決了將集合加載到內存中的需求? – Jeff

+0

@Jeff我真的會把它變成一個'DocumentSharing'聚合根,但想法是一樣的。它解決了不得不加載集合的需要,因爲'Document'沒有直接引用'用戶',它與其他用戶共享。一切都在'DocumentSharing'中,作爲一個聚合根具有自己的存儲庫,讓你知道給定的共享是否已經存在。此外,鏈接被建模爲ID引用(建議用於聚合到聚合關係),而不是完整的對象引用。 – guillaume31

+0

@ guillaume31我還沒有找到一個聚合根的具體例子,你介意提供一個'DocumentSharing'如何聚合的小例子嗎?這聽起來像我需要的。 – Jeff