7

我想在我正在使用的应用程序中开始更多地使用存储过程。该应用程序搜索十几个数据库。应用程序将信息存储在自己的数据库中。

我正在考虑将业务逻辑偏移到特定数据库中的存储过程。因此,如果逻辑对所有外部数据库都是通用的,则将其保存在应用程序 (.NET) 中。如果逻辑特定于数据库,则创建一个存储过程。

我不确定 SOLID 如何与存储过程和函数一起工作,因为没有接口或抽象。以下帖子似乎建议您尝试合并查询: http: //ledgersmbdev.blogspot.co.uk/2013/02/building-solid-databases-interface.html。例如,如果一个存储过程有四个 SQL 语句,那么为什么不尝试将它们组合成一个 SQL 语句呢?这是帖子说的吗?这是一种可靠的方法吗?

4

2 回答 2

4

我正在考虑将业务逻辑偏移到特定数据库中的存储过程。因此,如果逻辑对所有外部数据库都是通用的,则将其保存在应用程序 (.NET) 中。如果逻辑特定于数据库,则创建一个存储过程。

这句话引起了我的极大关注。当您说逻辑是否特定于数据库时,我认为您的意思是上述十二个中的一个,这是一个设计缺陷。数据库就是这样,信息存储,它们不应该需要任何“特殊”逻辑来在暴露的任何视图结构之外访问它们。此外,如果需要以非设置方式操作数据,则需要将其放入应用程序中。也就是说,当它们可以抵消您的应用程序时,不要让您执行计算。

当你设计你的应用程序时,你必须确保你没有改变你的数据库来忽略关系模型。以我的经验,这是使您的应用程序难以管理且速度缓慢的最佳方法之一。澄清一下,业务逻辑不应该存在于数据库中,这使得其他人很难使用您的数据。反对这一点的典型论据是,“我是唯一使用数据的人”,我说这是一个糟糕的设计原因。

继续前进,您应该尝试确定在关系(集合)模型中实际工作的内容,并构建一个可以查询该数据的应用程序。而不是构建适合您的应用程序的数据库。话虽如此,SOLID 不适用于关系模型,因为它们不是面向对象的
Wiki

在计算机编程中,SOLID(Single responsibility, Open-closed, Liskov substitution, Interface segregation and Dependency inversion)是Michael Feathers为早期Robert C. Martin 1 [2]提出的“前五原则”引入的助记词缩写。2000s[3] 代表面向对象编程和设计的五个基本原则。

评论更新

应用程序链接来自不同系统的信息并决定何时可以删除一组记录(这些是适用于所有系统的通用规则)。然后,在删除之前必须应用特定于数据库的规则。我正在考虑将本地业务规则偏移到存储过程。——</p>

看看这个评论,我不完全理解在审计/软删除之外会有哪些特定于数据库的规则。我同意可以将数据库特定规则设置为数据库中的管理存储过程,当您遇到以下问题时,您必须划清界限:

我的应用程序查询历史数据,除非它比6 months那时更旧,它必须从离线存储中检索。

之后删除此数据的选项6 months是允许应用程序通过某些业务逻辑清除它,或者创建一个计划任务来执行此清理,因为它是删除元组的正常数据库操作。

我在这里的论点是将它放在数据库中并禁止您的应用程序调用这些过程。事实上,您的应用程序甚至不应该知道在正确抽象的应用程序中存在数据库。因此,如果这是您提出的示例,那么我的解决方案如下:

1) Create stored procedures in the database that only a maintenance based user can invoke,   NOT THE APPLICATION  
2) Create a database scheduled task to run these based on your data needs.  
于 2013-09-08T22:40:02.637 回答
1

我同意不将业务逻辑放入 SQL 的概念以及 SQL 作为基于集合的语言的概念。

将 SOLID 视为一些非常好的编程实践的 OO 特定实现,并将它们应用于您编写的任何代码,包括 SQL。良好的表模式设计将包含 SOLID 思想。

我花了太多时间调试大型复杂的存储过程,所以如果你能避免它,请这样做。如果您必须编写存储过程代码,那么 SOLID 可以帮助您。

例如,我有一个大型存储过程,它查询几十个表,它返回一个数据集供打印机使用以发送 'welcome letters'。这是伪装成“报告”并用 SQL 编写的业务流程的典型示例。

从 SOLID 镜头看这个操作:

它应该有一个单一的责任和一个单一的改变理由。

该代码既确定谁收到了一封信,也确定了该信中包含哪些数据。如果过程的任一方面发生变化,您必须更新并重新测试整个系统。一个可靠的原则是有一个函数来确定谁收到一封信以及信中的内容。

这可以像使用单个查询返回客户列表一样简单。将其作为 ID 或 TVP 数组输入另一个存储过程以收集数据。

打开关闭

在大多数 SP(存储过程)代码中,整个过程要么是硬编码的,要么是“数据驱动的”。数据驱动通常是针对这一原则的 SQL 努力。SQL 流由 CASE 修改,该 CASE 将字段值或获得 EVAL 的文本字段中的 SQL 进行透视。

在我们刚刚创建的“主”SP 中可以看到解决我的问题的可靠方法 现在我有一个调用GET_CUSTOMERS的 SP和另一个调用GET_DATA的 SP 。这个主人基本上控制着工作流程,那个工作流程不应该改变。主站已关闭以进行修改。

如果我们随后需要增强系统以发送电子邮件,并且电子邮件具有不同的数据字段,那么我可以将调用GET_DATA替换为调用GET_DATA_FOR_EMAIL开放修改

主人只有一个责任。如果工作流程发生变化,则此主节点会根据规则 #1 发生变化。

里氏替换原则

这非常 OO,但是为了说明一点,让我们假设GET_CUSTOMERS SP 在这里可以被视为可替换的对象。我应该能够进行不同的查找,比如GET_CUSTOMERS_WHO_NEED_DIFFERENT_LETTER并且能够重用我的代码。或者也许GET_CUSTOMERS_FOR_INTERNAL_TEST?如果系统的所有组件都是一次性的且不可重复使用,那么系统将更难维护和理解。这可能是一个延伸。少考虑 OO 对象继承,多考虑可重用的具有相似参数的函数。

依赖倒置原则

这是我见过的最大的失败。不要围绕选择和 where 子句编写代码。我的系统以 *SELECT * FROM table, table, table where customers.letter-sent=false* 开始(实际上超过 1k 行),我希望以Find all customers and send them a letter结束。从抽象和代码开始,最高级别控制 SP 中的工作流。

这个想法是抽象是“查找客户”并返回客户列表。我应该能够将该逻辑交换为代码中的其他内容。我经常看到的违规行为是让“查找客户”写入连接到客户表上的表,然后另一个 SP 读取该表。在这种情况下,您的抽象会泄漏到实现中,并且您从可重用代码转到为特定表发送信件的系统。

SQL 非常强大,并且很容易将抽象中应该分开的步骤组合起来,以便在实现中混合在一起。

存储过程是一个很好的工具,它可以帮助团队用可读性换取性能,并以牺牲可伸缩性为代价带来便利。它仍然是代码,从其他语言中窃取模式只会在您的整体设计时有所帮助。

如果您需要一个简单的设计模式,通常您不需要设计模式。如果您的代码超过 50 行左右,请尝试将其移至应用层和/或考虑进行可靠的重构。

于 2014-01-12T14:48:42.853 回答