我试着首先在StakOverflow上以更具体的方式问这个问题,但在这里指出之后,我意识到我应该用更一般的术语重新表述它;但是,如果您想要更具体地了解我的具体用例,仍然可以查看原始问题。
假设一个相对复杂的报告界面。为了简单起见,假设我们只以表格的形式显示结果数据,但我们显示的是一个不映射到现有域对象的聚合。用户可以从日历中选择开始/结束日期,可以根据特定的标准限制他们的报告,并可以按特定的列排序结果;所有这些都是可选的。它们的请求由几层代码处理,但经过处理后,我们得到一个包含筛选条件、排序指令和联接语句的SQL查询,以便聚合来自多个表的数据。
在数据库端,我使用了一个ORM (NHibernate),它处理持久性并强制我实现严格的实体/域对象;这是明确的,而且已经很好了。
在另一端(面向用户),我有一个向用户展示东西的视图,以及一个解释输入/输出数据的控制器。这也很清楚,而且处理得当。
我的问题是:这些层之间应该发生什么?建议的正确封装方法是什么?我的直观方法是允许我的业务逻辑(BL)调用各种查询助手,这些查询帮助程序根据用户的输入逐步构建查询对象的各个部分。当我不得不在BL中有条件地处理联接时,它就开始发出危险信号,这显然是不恰当的。
我同意Frédéric对我的关于StackOverflow的前一个问题的回答:将构建查询的所有代码移到更深、更接近持久层,并使BL不受任何有关模型的知识的影响。这无疑是一种更清晰的方法,我可以很容易地定义一个数据传输对象(DTO),以便将数据库结果“向上”地从持久化级别传递到控制器,并最终传递到视图层。但是,用户可以填写大量的可选输入;这意味着我还需要一个辅助类,从控制器层到BL所在的服务级别,然后一直到持久化层上的查询帮助器本身。我不知道它们被称为什么,所以让我们将它们称为“QTO”,用于“查询传输对象”( "Query“在”用户查询“的意义上,而不是在SQL的意义上)。因此,持久化级别将将这些QTO解释为SQL,它将执行SQL,并将结果集转换为气泡备份的DTO。
这种方法的问题是,我只是为数据传输添加了几个中间类,但是处理本身也是一样的(只在一个不同的层上)。我仍然需要有条件地添加所有这些联接,有条件地添加条件条件,添加排序指令--只有现在,我才需要在控制器中有条件地填充QTO!未来的重构并不容易: QTO结构中的任何更改都会影响控制器层和模型层,而且几乎可以保证对数据库结构的任何相关更改都会导致QTO和DTO的更改。
根据你的经验,什么是设计这个的好方法?我是不是设计得太过火了?在您使用的/建议的设计中,我的复杂性问题是如何解决的呢?或者说,我没有看到这样的好处可以抵消增加的复杂性?
发布于 2016-03-14 15:16:05
你是明智的,要警惕过度工程的事情。不过,听起来您的“复杂查询”问题上下文可能是投资解释器模式的一个很好的匹配,以便帮助您抽象(在BL中)使用持久化层进行查询吗?
然后问题变成“哪种查询语言的解释器?”因此,我将回到“盯着”各种连接的形状--您最终会用前面提到的第一种方法在BL中写作--我将尝试设计一个简单的DSL,它可以成为您域上最常见/最常见的查询模式的源查询语言。
或者(既然您提到了NHibernate,带有一个"N"),如果您发现Linq的理解语法强大到足以构建这些查询,也可以实现自己的QueryProvider。
发布于 2016-03-14 15:47:20
这里有一个可能的设计:
我会把它写成两个方面:
映射将处理从BL/DTO到DB对象的映射、关系构建等。请注意,对于IN,您将需要映射和构建,对于OUT (返回),您只需要映射,因为在往返/
查询生成将处理以前映射的对象以生成SQL查询。sql查询是由部分组成的,因此您也可以对其进行分解:
可以将其简化为一个层,但我认为您需要业务对象(DTO)和数据库(映射层)之间的抽象区域。
然后,可以从映射层分别测试查询的构建。另外,如果有映射更改,则应该在映射层中处理这一点,然后构建层应该无缝地处理更改,因为它只是从以前映射的对象构建查询。
例如,假设我在数据库中添加了另一个联接表。映射层应该处理这个问题,这将自动处理另一个连接条件。连接的实际构建应该没有什么不同,但是不是1连接条件,而是现在我有2。
For each (Condition in JoinConditions)....这也使得每一层都是可测试的,而且每一层都具有单一的责任。
我要警告的是,我很少看到这些情况,而且它们本身就变得复杂起来。从一些常见的操作开始,在构建一个更复杂/抽象的操作之前,先从一些简单的逻辑开始。
发布于 2016-03-14 16:09:27
基本上,您需要从UI层到DB的查询构建器机制或映射器。
由于您已经使用了一个很好的ORM,您只需要构建映射部分,NHibernate将负责查询构建。
对于映射部分,您需要获取所有的UI特定参数,并将查询构建器逻辑放到最后返回结果。对于UI的任何更改,您都需要从头开始运行它。
关于查询构建部分,我建议您阅读这篇关于NHibernate QueryOver 查询构建技术的文章。
UI特定的代码应该在UI层上,特定于数据的代码应该在存储库或DAL层上。但这两层又是如何沟通的呢?这是最棘手的部分。
我经常为我的(DB)实体和接口使用额外的层,而且只为通信目的使用模型,这在我的所有项目(UI、Business、Service等)中都是常见的。但请考虑只保留项目(UI、业务、服务等)。在特定项目中的模型。
因此,在实体层fpr到DAL/Repository映射中创建一个通用模型,将其填充到UI层(我以前称之为映射),而不是发送到DAL/Repository层。有了这些信息,您就可以构建查询、执行结果并将结果发送回UI层。
我希望这个方法对你有帮助。
https://softwareengineering.stackexchange.com/questions/312697
复制相似问题