DDD 逻辑和持久性无知
DDD logic and Persistence Ignorance
考虑以下场景:
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 将整个集合延迟加载到内存中。我 可以 在应用程序服务中进行存在性检查,但我考虑到这个域逻辑,所以对我来说,将它保存在文档 class 中最有意义。
我似乎无法弄清楚应该如何使用 DDD 来完成这项工作?如果我无法使用 ORM 怎么办,如何做这类事情?
我想我应该有一个 Document Aggregate 和一个 User Aggregate?
我查看了各种 DDD 资源(虽然我还没有读过这本书),但我似乎无法找到这个特定场景的答案。
我想如果你在基于 paper/actor 的世界中对此进行建模,那么有人将负责编组谁可以访问哪些文档,这可能依赖于某种基于纸张的文件人工制品。要获得对文档的访问权限,您必须填写文档申请表,这可能会经过审批流程。
在基于纸张的世界中,这种形式将成为多对多链接实体,成为用户访问安全文档的关键。这将使 User
、Document
和 DocumentRequestForm
成为三个独立的实体。
这很快就完成了,所以它并不完美,但你明白了它的要点:
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);
}
}
考虑以下场景:
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 将整个集合延迟加载到内存中。我 可以 在应用程序服务中进行存在性检查,但我考虑到这个域逻辑,所以对我来说,将它保存在文档 class 中最有意义。
我似乎无法弄清楚应该如何使用 DDD 来完成这项工作?如果我无法使用 ORM 怎么办,如何做这类事情?
我想我应该有一个 Document Aggregate 和一个 User Aggregate?
我查看了各种 DDD 资源(虽然我还没有读过这本书),但我似乎无法找到这个特定场景的答案。
我想如果你在基于 paper/actor 的世界中对此进行建模,那么有人将负责编组谁可以访问哪些文档,这可能依赖于某种基于纸张的文件人工制品。要获得对文档的访问权限,您必须填写文档申请表,这可能会经过审批流程。
在基于纸张的世界中,这种形式将成为多对多链接实体,成为用户访问安全文档的关键。这将使 User
、Document
和 DocumentRequestForm
成为三个独立的实体。
这很快就完成了,所以它并不完美,但你明白了它的要点:
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);
}
}