CQS文件&名称空间组织

时间:2017-10-15 06:34:27

标签: orm architecture

我有一个非常大的解决方案,由50多个项目(Windows服务,网站,类库)组成,这些项目是使用服务框架构建的。存储库和使用Telerik DataAccess ORM。经过与Telerik架构师的长时间讨论后,他们提出了上述设计,因为它应该符合我们当时的需求并提供可测试的代码框架。几年后,现在我们的服务类已经发展到数千行,这使得它们难以维护和测试。

在阅读和研究我们的代码中的一些问题后,我遇到了CQS(命令查询分离),这对我在项目中更有意义,因为它将我们庞大的服务类划分为更小的可测试类。我已经成功地实现了一个小概念,但我现在想知道当我将1000多个查询移动到CQS命名空间时,我的代码将如何组织(现在专注于查询,因为我想象的命令将组织相同) - 显然所有查询,处理程序和结果都在他们自己的文件夹中,每个文件夹将有1000多个文件将是一个巨大的痛苦找到的东西。

到目前为止,我已经拥有了这个文件夹结构

    Model
        Customer
    Queries
        CustomerNameByIdQuery
        CustomerNameByTextSearchQuery
    QueryHandlers
        CustomerNameByIdQueryHandler
        CustomerNameByTextSearchQueryHandler
    QueryResults
        CustomerNameQueryResult

两个查询都返回相同的CustomerNameQueryResult,其中只有Id和Value属性

现在成像我需要查询完整的客户记录,因此我需要CustomerByIdQueryCustomerByIdQueryHandler以及Customer模型的结果。目前,对于不同需求的不同参数,客户还有大约10个其他查询。

数百个表上的这种模式会使很多查询类和处理程序很难找到我需要在代码中的特定位置使用的内容(如果可能,可以促进代码重用)。

我正在寻找那些在一个大型制作应用程序中使用CQS的老手的建议,关于项目中查询的命名空间/文件的组织,以及如何为查询/处理程序/结果组织您的解决方案?例如,你把查询&处理程序在同一个文件中?单独的文件是单独的目录?对于同一对象的多个查询,您如何处理?单个文件包含所有查询或多个文件?您是否将查询与命名空间分开以便于编码?您的结构是否存在任何问题?

我知道没有单一的"正确的"回答这里,但是一段时间以来一直使用这种方法的人的一些建议会帮助我和其他人陷入你已经遇到的任何问题并在文件/文件夹结构中解决。

2 个答案:

答案 0 :(得分:0)

我们不使用CQS,但我们有很多项目的大解决方案,其中一个项目是存储库层。

我们每个模型都有一个存储库类,所以按照您的示例,我们尝试让所有查询都相对于 Customer Repository 中的 Customer Model 。 实际上,这来自我们使用的层中的模型/代理/服务/存储库架构。 技巧部分包含联系人之类的内容。我们有那个模型和那个使用Contacts表充满查询的存储库。 那么来自Customeres查询的联系人在哪里呢?这取决于功能,它实际上是客户的功能,因此它被放置在客户存储库中。请注意,我们可以选择客户,并在循环中为每个客户调用联系人,但通常我们会避免由于性能问题。

相对于许多GetCustomerXXX方法。

是的,我们已经有了很多方法,我们也有相同方法的重载,允许使用大量不同的参数进行更精细的搜索,还有一个分页查询Wraper,所以我们每次用户向下滚动时都会有一个搜索屏幕加载(所以我们根据你的要求改变答案。所有这些查询都在Customer存储库中。

相对于域名组织。

每个模型都可以被任何项目/域中的许多功能使用。例如,客户位于核心域中,而财务域中的账单位于核心域中。当然,您可以查询客户的所有帐单,以便任何项目/域都可以使用客户模型。使用客户和账单的所有项目都将具有相同的域和几乎相同的文件夹层。

恢复: 我们有一个模型项目,它被所有其他项目引用。它包含许多域,也包含复杂的文件夹层次结构。

所有项目(服务和存储库除外)都引用代理项目,代理引用服务,服务引用存储库。

这样我们就不会获得循环引用,管理复杂性并保持理智。 注意(使用代理的一些UI项目甚至使用不同的语言,并用于实现不同的用户体验(web / winform / remote / offline)。

修改

有些服务可能会变得类似于monolithc怪物。恕我直言,超过300线的东西很大,有些服务增长到超过2k线。

一个因素帮助了我们很多,已经构建基类来处理最常见的场景。

例如,我们在存储库基类中有一个IEnumarebale<BaseModel> GetAll(GenericFilter filter)方法,这意味着所有模型都会自动实现。 事实上,大多数repositoy类都是空的,因为基本已经被覆盖,我们只需要编写自定义逻辑。

此外,我们避免从另一个服务调用存储库,在一般服务调用服务中,从而避免几乎任何代码重复。例如,Base Crud Class具有CustomValidation方法和Before / After Insert / Delete / Update事件,我们可以根据需要覆盖。

按照上一个例子。 Customer和Contacts都具有CustomValidation。插入新客户(使用联系人)将自动调用两个验证。

所有自动化测试都会调用基本方法,从而节省大量时间。

答案 1 :(得分:0)

实际上,我采用了不同的方法。相反,我将所有功能按功能分组。由于每个模型(请求/响应)都直接绑定到处理程序,因此我将它们全部放在同一文件夹中。由于每个查询也都必须特定于您正在运行的查询/命令,因此我也将支持它的存储库也放在了同一文件夹中。因此,我将以这样的方式构造您的示例:

型号 顾客 特征 查询 CustomerNameById CustomerNameByIdQuery CustomerNameByIdQueryHandler CustomerNameByIdQueryResult CustomerNameByText CustomerNameByTextSearchQuery CustomerNameByTextSearchQueryHandler CustomerNameByTextSearchResult

但是,如果Id / Text最终返回的是完全相同的东西,我可能会合并两者,而只是更改Query / Request以同时支持两个过滤器。没有错。根据我的经验,您正在编写的查询是针对特定前端的,该前端以特定方式显示结果。

我在CQS项目中也大量使用装饰器,因此,如果您具有验证或映射等功能,这些装饰器也可以放入同一子文件夹中。这样,特定于手头的查询/命令的所有内容都在同一文件夹中。如果需要外部引用,则可以IoC注入该共享组件,并且还是不错的选择。