寻找示例,似乎所有内容(如LinQ)都面向SQL数据库.但是对于许多NoSQL数据库而言,大多数查询,即使只是“select * from table”也需要预定义的视图.然而,如果存储库正在映射Web服务,即使查询类型更加严格.
考虑到非SQL数据库的局限性,是否存在规范模式的变体?我觉得这需要使用继承和静态声明来支持不同类型的持久性后端.
我应该如何在我的存储库中组合“排序”和“过滤”?作为示例,请考虑存储库以获取Order项列表.
(Query)findAllSortedByDate; (Query)findAllSortedByName; (Query)findAllSortedByQuantity;
因此,当由表格显示时,这些是不同类型的排序.由于我可能会处理大量结果,因此我从不考虑在视图或视图模型中进行排序或过滤.最初我想到了一个Proyection类,它根据用户操作从存储库中选择正确的查询.但是,如果我想在不同的过滤器中组合不同的排序策略,这不会很好.
显然我需要某种类型的“规范”对象,但我不确定是否:
>我应该将它们用于我的存储库,让它们变得更聪明吗?或者我应该将它们用于我的视图模型?
>如何正确限制我的规范对象以获得良好的多语言持久性?
最初我考虑使用Repository作为collection-like interface执行任何查询,但现在我注意到视图模型也可能表现为“有状态”类似集合的接口,而前者是“无状态”类似集合的接口.
>总的来说,我应该尝试在我的存储库中保留任何类型的排序/过滤?如果所有查询结果都可以加载到内存中,那么这样做可能会增加不必要的复杂性.
更新:
为了更好地解决这个问题,还要考虑尽管NoSQL视图可以被过滤和排序,但是全文搜索可能需要外部索引引擎,例如Lucene或SQLite-FTS,它们只为必须排序的查询提供实体的唯一标识.并再次过滤.
通过“类似集合的接口”,Fowler并不意味着暴露出类似于数组或列表的API的东西:例如,它与ICollection<T>
无关!存储库应该是encapsulate持久层的所有技术细节,但是应该定义其API以使其在业务域中具有表现力.
您应该将规范视为在域中相关的逻辑谓词(实际上它们是域模型中的一等公民),它们既可以用于检查实体的不同质量,也可以从一个集合中选择实体(一组可以是一个存储库或一个简单的列表).例如,在我为意大利银行设计的金融领域模型中,我有DurationOfMBondSpecification,StandardAndPoorsLongTermRatingSpecification等等.
实际上,在DDD中,规范来自业务需求(通常是合同边界),必须由软件在其运营期间强制执行.它们可以用作过滤器的抽象,但这更像是一个幸运的副作用.
关于分类
大多数时候排序(和切片,分组……)只是一个表示问题.当这是一个业务问题时,应该从域专家的知识中提取适当的比较器(以及石斑鱼等)作为域概念.
然而,即使只是一个演示问题,在存储库中处理它也要高效得多.
在.NET上,一个可能(并且非常昂贵)解决这些问题的方法是编写一个自定义LINQ提供程序(或多个),它可以将所有可以用无处不在的语言表达的查询转换为所需的持久层.然而,这个解决方案有一个主要的缺点,如果你不能从开头翻译所有的查询,你永远无法估计使用域更改应用程序的工作:时间将到来,你将不得不深入重构QueryProvider来处理一个新的复杂查询(这样的重构将花费你比你能承受的更多).
为了解决这个问题,在(正在进行和非常雄心勃勃的)Epic框架(免责声明:我是核心开发人员)中,我们选择加入规范模式和查询对象模式,提供一个通用API,客户端使用规范进行过滤,使用比较器进行排序以及使用整数进行切片,而不会产生LINQ的(不可预测的)成本.
在Fowler方案(下面)中,我们将规范(aCriteria)和辅助排序要求传递给存储库:
作为替代方案,您可以使用custom repositories:如果您没有数千种不同类型的查询,那么它是迄今为止更便宜的方法.
奖金解决方案
一个快速但仍然正确的解决方案是“只是”使用您的持久性语言作为查询. DDD用于复杂的操作边界和查询(大多数情况下)不起作用:因此您可以简单地使用SQL或NoSQL数据库提供的语言来检索您需要的投影和/或您需要操作的实体的identifiers.您将看到您查询的数据集与确保域不变量所需的数据集非常不同!
这就是为什么,例如,有时,序列化文件可能是解决手头问题的域持久性的最佳方法.毕竟,什么是文件系统,如果不是最分散的NoSQL数据库!