【问题标题】:DDD logic and Persistence IgnoranceDDD 逻辑和持久性无知
【发布时间】:2015-08-17 22:22:49
【问题描述】:

考虑以下场景:

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,该怎么办?

我想我应该有一个 Document Aggregate 和一个 User Aggregate?

我查看了各种 DDD 资源(虽然我没有读过这本书),但我似乎无法找到这个特定场景的答案。

【问题讨论】:

  • 这种类型的逻辑可以/应该属于域服务。只是处理此类事情的另一种战术模式。你的仓库可以有一个简单的方法返回一个关于文档是否已与特定用户共享的布尔值
  • @Marco 那么我无法理解域类本身应该采用什么样的逻辑?我描述的不是领域逻辑吗?
  • 聚合强制执行的一致性边界是它的不变量。您的业​​务用户真的说这是业务规则还是您自己提出的?
  • 您也可以使用双重调度,传入的用户可以包含与其共享的文档 ID 的集合,并且文档可以在那里进行检查
  • 我想出了这个 - the consistency boundary an aggregate enforces is its invariants 不确定这是什么意思?

标签: c# .net orm domain-driven-design persistence


【解决方案1】:

我想如果您在基于纸张/演员的世界中对此进行建模,那么有人将负责编组有权访问哪些文档的人,这可能依赖于某种基于纸张的人工制品。要访问文档,您必须填写文档申请表,该表可能会通过审批流程。

这种形式在纸质世界中,将成为多对多链接实体,成为用户访问安全文档的关键。它将使User、Document 和DocumentRequestForm 成为三个独立的实体。

【讨论】:

  • 就我而言,它就像问题中指定的那样简单。但是,我看不出将它拆分为 DocumentRequestForm 是如何解决将集合加载到内存中的需要的?
  • @Jeff 我真的会把它设为DocumentSharing 聚合根,但想法是一样的。它解决了必须加载集合的需要,因为Document 没有直接引用它与之共享的Users。一切都在DocumentSharing 中,作为聚合根有自己的存储库,让您知道给定的共享是否已经存在。此外,链接被建模为 ID 引用(建议用于聚合到聚合关系)而不是完整的对象引用。
  • @guillaume31 我找不到聚合根的具体示例,您介意提供一个DocumentSharing 聚合的小示例吗?这听起来像是我需要的。
  • DocumentSharing 是一个很棒的名字。聚合根是一个实体或实体树(可能包含也可能不包含任何数量的值对象)。在Order 示例中,Order 实体将包含OrderItem 实体的集合,但OrderItem 实体将无法从您的数据库中访问或检索。如果没有Order,它们将没有上下文,因此Order Aggregate Root 将作为一个对象加载/保存/修改。 IE。 Order 会有一个存储库,而OrderItem 没有。
  • @guillaume31 您的DocumentSharing AR 是否会通过 id 引用 User 和 Document?
【解决方案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);
    }
}

【讨论】:

  • 评论不用于扩展讨论;这个对话是moved to chat。
  • 要改变的东西太多了,我也对所有东西都进行了测试,所以工作量增加了 4 倍!
  • 现在我不再有实体之间的引用,如何将其映射到我的 SQL 数据库中的关系?抱歉拖了这个:P
  • 这是我采用的方法。
猜你喜欢
  • 2013-08-23
  • 1970-01-01
  • 1970-01-01
  • 2015-09-17
  • 1970-01-01
  • 1970-01-01
  • 2020-01-11
  • 2014-08-11
  • 1970-01-01
相关资源
最近更新 更多