【问题标题】:DDD and Entity Framework, FiltersDDD 和实体框架、过滤器
【发布时间】:2015-02-18 20:44:06
【问题描述】:

所以当我们谈论过滤和查询时,我正在努力解决 DDD 必须遵循的方法。从这个 SO 问题Is it okay to bypass the repository pattern for complex queries? 我可以看到用户的过滤应该在获得所有产品后完成。接受的答案中的代码是:

Products products = /* get Products repository implementation */;
IList<Product> res = products.BoughtByUser(User user);

但是等等,如果数据库有 100 万个产品呢?像这样直接在数据库中执行此过滤器的最佳方法不是:

productsRepository.Find(p => p.User.Id == userId);

但根据我对 DDD 的实际了解,这是错误的,因为这个逻辑应该在产品本身内部。

那么,如何处理这种情况?

【问题讨论】:

    标签: performance entity-framework domain-driven-design


    【解决方案1】:

    我同意 Yorro 的回答。根据评论, products 确实是一个存储库。 不过,可以进一步探讨有关底层数据结构的性能与在应用程序中保留领域知识的问题。 数据库非常擅长过滤和查询数据,它们为此进行了优化,而对于我们来说,仅仅为了“将我们的知识保留在领域中”而忽略这一点是幼稚的。

    您的示例显示了存储库专业化,尽管很冗长,但它很好。 那个搜索的逻辑被那个调用封装了,只要调用那个方法的接口在域中,在数据层实现,一切都好。 实际上,调用可能是对执行非常复杂操作的存储过程。 (在这种情况下,是的,您的一些逻辑已经脱离了该领域,但您是有意识地做出的,如果您引入另一种数据技术,您将不得不再次实现该功能。)

    还有一个选择... 我们可以将搜索逻辑封装在规范 (http://en.wikipedia.org/wiki/Specification_pattern) 中,并将规范从我们的域逻辑代码传递到我们的存储库,后者将解释规范并进行查询。 这使得我们的域忽略了底层数据结构的工作原理,但它可以控制搜索条件是什么。

    我通常发现自己实现了存储库专业化的混合,并拥有一个接受 ISpecification 以进行更轻量级查询的基础存储库。

    【讨论】:

    • 如果我的RepositoryBase 中有一个Find 方法,它需要一个表达式作为参数,并且来自域中的实际ProductService 类,我会调用Find 方法传递拉姆达。这是一个好方法吗?我的意思是,逻辑不在Product(实体)类本身内部,而是在属于域的ProductService内部。
    • 是的,没关系。根据系统的规模,您仍然可以考虑规范的方法并将这些搜索表达式封装在规范中。如果您阅读 ISpecification,您会注意到执行 And / Or 等并有效链接它们的能力使它们如此强大。表达式已经非常强大,将它们用作规范模式的底层查询机制非常棒。 (查看 joseph albahari 的 LinqKit。他在博客中讲述了他是如何编写它的,这提供了一些非常有价值的见解。)
    【解决方案2】:

    根据您的链接,Products 类是存储库,只是在没有“存储库”后缀的情况下命名。

    过滤器应该在数据库中是正确的,因为您在域中,所以您看不到它。

    第一种方法和第二种方法是一样的。不同的是,第一个更符合 DDD 因为正确使用了ubiquitous language

    // First example
    // Take note, the products IS the repository
    IList<Product> productsByUser = products.BoughtByUser(User user);
    
    // Second example
    IList<Product> productsByUser = productsRepository.Find(p => p.User.Id == userId);
    

    如果您深入数据访问层,您可以看到您正在谈论的过滤。

    public IList<Product> BoughByUser(User user)
    {
        IList<Product> products = this.dbContext.Products.Find(p => p.User.Id == user.ID);
    
        return products;
    }
    

    【讨论】:

    • 同意。我会这样做:如果我需要在单个对象的属性之上进行验证,我会将此方法放在实体中。否则,如果我需要基于一个查询字符串查询多个对象,我会使用存储库方法。你怎么看?
    【解决方案3】:

    这不是您问题的直接答案(Yorro 的回答是正确的),但也许它可以帮助您更好地理解 DDD。这是一个“错误的方式,回头”的答案。

    您的视图不需要域规则;不需要包含 100 万个子项或 100 万个实体的聚合。因此,您不需要“绕过”产品存储库,因为您应该拥有“查看服务”和“查看存储库”,它允许您查询(和分页等)非规范化数据的持久性以获取您的视图。

    当需要更新/插入/删除时,您应该使用聚合/实体应用域规则。

    一旦用户从 100 万个列表中选择一个或多个产品并按下例如删除按钮,您应该使用产品存储库来检索所选产品的聚合/实体,应用删除规则和不变量并持久保存。

    【讨论】:

      猜你喜欢
      • 2017-05-21
      • 2017-02-11
      • 1970-01-01
      • 1970-01-01
      • 2011-02-25
      • 1970-01-01
      • 2017-11-08
      • 2015-12-29
      • 1970-01-01
      相关资源
      最近更新 更多