DDD 和 Entity Framework,过滤器
DDD and Entity Framework, Filters
所以当我们谈论过滤和查询时,我正在努力解决 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 的实际了解,这是错误的,因为这个逻辑应该在产品本身内部。
因此,如何处理这种情况?
根据您的 link,Products
class 是 存储库,只是命名时没有 "repository" 后缀。
您认为过滤应该在数据库中是正确的,您只是看不到它,因为您在域中。
第一种和第二种方法是一样的。不同之处在于,由于 ubiquitous language
的正确使用,第一个更符合 DDD
// 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;
}
我同意约罗的回答。根据评论,产品确实是一个存储库。
不过,可以进一步探讨有关底层数据结构性能与在应用程序中保留领域知识的问题。
数据库非常擅长过滤和查询数据,它们经过优化可以做到这一点,对于我们来说,简单地忽略这一点 "keep our knowledge in the domain" 是天真的。
您的示例显示了存储库专业化,虽然冗长但很好。
该搜索的逻辑由该调用封装,只要调用该方法的接口在域中,并且在数据层中实现,一切都很好。
实际上,调用可能是对执行非常复杂操作的存储过程的调用。 (在这种情况下,是的,您的一些逻辑已经逃脱了域,但您是有意识地做出这个决定,如果您引入另一种数据技术,您将不得不再次实现该功能。)
还有一个选择...
我们可以将搜索逻辑封装在规范 (http://en.wikipedia.org/wiki/Specification_pattern) 中,并将规范从我们的领域逻辑代码传递到我们的存储库,存储库将解释规范并进行查询。
这使得我们的域忘记了底层数据结构的工作方式,但它可以控制搜索条件。
我经常发现自己实现了存储库专业化的混合,并拥有一个接受 ISpecification 的基础存储库以进行更轻量级的查询。
这不是您问题的直接答案(Yorro 的回答是正确的),但也许它可以帮助您更好地理解 DDD。这是一个 "wrong way, turn back" 的回答。
您的观点不需要域规则;不需要包含 100 万个子项或 100 万个实体的聚合。因此,您不需要 "bypass" 产品存储库,因为您应该有 "View Services" 和 "View Repositories",这允许您查询(和分页等)非规范化持久化数据以供您查看。
当需要 update/insert/delete 时,您应该使用 aggregates/entities 应用域规则。
一旦用户 select 100 万列表中的一个或多个产品并按下删除按钮,您应该使用产品存储库检索 aggregate/entity 的 selected产品,应用删除规则和不变量并永久保存。
所以当我们谈论过滤和查询时,我正在努力解决 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 的实际了解,这是错误的,因为这个逻辑应该在产品本身内部。
因此,如何处理这种情况?
根据您的 link,Products
class 是 存储库,只是命名时没有 "repository" 后缀。
您认为过滤应该在数据库中是正确的,您只是看不到它,因为您在域中。
第一种和第二种方法是一样的。不同之处在于,由于 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;
}
我同意约罗的回答。根据评论,产品确实是一个存储库。 不过,可以进一步探讨有关底层数据结构性能与在应用程序中保留领域知识的问题。 数据库非常擅长过滤和查询数据,它们经过优化可以做到这一点,对于我们来说,简单地忽略这一点 "keep our knowledge in the domain" 是天真的。
您的示例显示了存储库专业化,虽然冗长但很好。 该搜索的逻辑由该调用封装,只要调用该方法的接口在域中,并且在数据层中实现,一切都很好。 实际上,调用可能是对执行非常复杂操作的存储过程的调用。 (在这种情况下,是的,您的一些逻辑已经逃脱了域,但您是有意识地做出这个决定,如果您引入另一种数据技术,您将不得不再次实现该功能。)
还有一个选择... 我们可以将搜索逻辑封装在规范 (http://en.wikipedia.org/wiki/Specification_pattern) 中,并将规范从我们的领域逻辑代码传递到我们的存储库,存储库将解释规范并进行查询。 这使得我们的域忘记了底层数据结构的工作方式,但它可以控制搜索条件。
我经常发现自己实现了存储库专业化的混合,并拥有一个接受 ISpecification 的基础存储库以进行更轻量级的查询。
这不是您问题的直接答案(Yorro 的回答是正确的),但也许它可以帮助您更好地理解 DDD。这是一个 "wrong way, turn back" 的回答。
您的观点不需要域规则;不需要包含 100 万个子项或 100 万个实体的聚合。因此,您不需要 "bypass" 产品存储库,因为您应该有 "View Services" 和 "View Repositories",这允许您查询(和分页等)非规范化持久化数据以供您查看。
当需要 update/insert/delete 时,您应该使用 aggregates/entities 应用域规则。
一旦用户 select 100 万列表中的一个或多个产品并按下删除按钮,您应该使用产品存储库检索 aggregate/entity 的 selected产品,应用删除规则和不变量并永久保存。