业务逻辑:数据库或应用程序层

42

一直以来都存在这样的问题:应该将业务逻辑放在数据库中,作为存储过程(或程序包),还是放在应用程序/中间层中?更重要的是,为什么?

假设数据库独立性不是目标。


你可能应该给这个添加一个“主观”的标签。 - S.Lott
1
Martin Fowler关于这个主题的思考:http://martinfowler.com/articles/dblogic.html#DomainModel - Nathan Long
@NathanLong,他有一个坏习惯,就是把事情搞得比必要的更长。 - Pacerier
24个回答

38

在确定业务逻辑应该放到哪里时,代码的可维护性总是一个大问题。

集成的调试工具和更强大的IDE通常使得中间层代码比存储过程中的同样代码更易于维护。除非真有必要,否则您应该从中间层/应用程序开始编写业务逻辑,而不是存储过程。

然而,当涉及报告、数据挖掘/搜索时,存储过程往往是更好的选择。这要归功于数据库聚合/过滤能力的强大以及处理非常接近数据源的事实。但是这可能并不是大多数人所考虑的传统业务逻辑。


在存储过程中,SELECT SUM(x) 的运行速度与应用程序层相同。不确定你回答的第三段是否真的必要。 - S.Lott
关于SUM函数,你说的当然是对的,但我真正比较的是使用DB函数与手动聚合Java/C#等语言的区别。此外,在多次网络往返成本高昂的情况下,存储过程确实有其应用场景。 - Ash
当另一个应用程序需要访问我用中间层(VB.NET)编写的业务功能时,我为其编写了一个存储过程以供调用。然后我注释掉了VB中间层代码,用存储过程调用替换了它。在集成开发环境中进行调试要容易得多,但有时您可能还有其他考虑。 - David
6
当聚合/报告大型数据集时,存储过程可以更快,因为数据集可以在服务器上本地处理,而不是被传递到中间层。但这是一种性能优化,可以等到必要时再考虑。 - sleske
2
@Sleske:我同意,你不应该在没有出现性能问题的情况下复杂化你的设计。我曾经犯过这样的错误,我不会再犯了。 - SDReyes
如果数据在内存中,与使用IO与服务器通信相比,速度会快得多。使用缓存进行重复设置,这样您甚至不需要访问数据库。而且,在C#/Java中,我们还有字典、哈希表、排序列表、二进制搜索、树等等,它们的速度比任何数据库服务器都快得多。更不用说通过使用相同的集合,您可以大大简化数据库逻辑/代码了。 - user1496062

32

将足够的业务逻辑放入数据库中,以确保数据的一致性和正确性。

但不要担心需要在其他层面上复制一些这些逻辑来增强用户体验。


3
我认为应该尽可能使数据库更加智能化,以确保数据完整性。应用程序和数据库通常存在多对多的关系,并且不能假设所有更新都会通过数据访问层进行。 - ConcernedOfTunbridgeWells
3
如果提升用户体验需要复制业务逻辑,那就进行复制(+1),不必担心这一点。这么做还可能有助于分布式系统的性能提升。 - nwahmaet
11
属于数据库的“业务逻辑”首先是表格的设计,希望这准确地代表了所述业务的数据需求。话虽如此,不要复制业务功能。从我的大量经验来看,原开发人员已离职的系统将缺乏文档——如果您更改某些逻辑,则不希望惊讶地发现必须在两个或更多地方进行更改。 - David
4
我也非常警惕重复逻辑。敏捷方法的核心是DRY不是没有原因的。但我并不确定这真的是必要的。我不认为在UI中防止约束违规算是“重复逻辑”…… - sleske
+1 ConcernedOfTunbridgeWells:这是在数据库层面强制执行数据完整性的一个很好的理由! - JohnB
1
Tom Kyte在这个主题上有一个很好的帖子:http://asktom.oracle.com/pls/apex/f?p=100:11:0::::P11_QUESTION_ID:2143974700346554115 - Ollie

30

对于非常简单的情况,您可以将业务逻辑放在存储过程中。通常即使是简单的情况随着时间的推移也会变得复杂。以下是我不将业务逻辑放在数据库中的原因:

将业务逻辑放在数据库中会紧密地耦合它与数据库的技术实现。更改表格将导致您再次更改许多存储过程,并引起许多额外的错误和测试。

通常 UI 对于验证等事项依赖于业务逻辑。将这些内容放在数据库中将导致数据库和 UI 之间的紧密耦合,或者在不同情况下在这两个方面之间重复验证逻辑。

在同一数据库上运行多个应用程序将变得困难。为一个应用程序进行的更改将导致其他应用程序出现问题。这很快就会变成维护噩梦。因此它并不能真正扩展。

更实际的是,SQL 不是用于以可理解的方式实现业务逻辑的好语言。 SQL 对于基于集合的操作非常好,但是它缺少“大规模编程”结构,很难维护大量的存储过程。现代面向对象语言更适合这种情况,并且更加灵活。

这并不意味着您不能使用存储过程和视图。我认为有时在表格和应用程序之间放置额外的存储过程和视图层是一个好主意,以将它们解耦。这样,您就可以在不更改外部接口的情况下更改数据库布局,从而使您能够独立地重构数据库。


11

只要你保持一致,真的取决于你。

把它放在数据库层面是一个好理由:如果你相当确定你的客户永远不会更改他们的数据库后端。

把它放在应用程序层面是一个好理由:如果你正在为你的应用程序针对多个持久化技术。

你还应该考虑核心能力。你的开发人员主要是应用程序层面的开发人员,还是主要是DBA类型的开发人员?


7
虽然没有一个正确的答案 - 这取决于具体的项目,但我建议采用Eric Evans在 "Domain Driven Design" 中提倡的方法。在这种方法中,业务逻辑被隔离在自己的层中 - 领域层 - 该层位于基础架构层之上(包括数据库代码),并位于应用程序层之下,后者将请求发送到领域层以实现并监听其完成的确认,从而驱动应用程序。
这样,业务逻辑被捕获在一个模型中,可以与那些了解业务而非技术问题的人讨论,并且应该更容易隔离业务规则本身的更改、技术实现问题以及与业务(领域)模型交互的应用程序流程。
如果有机会,我建议阅读上述书籍,因为它非常好地解释了如何在真正的代码和项目的现实世界中近似实现这个纯理想。

2
我读过这本书,但没有机会实践。你试过吗? - darpet

7

任何影响数据完整性的事情都必须放在数据库层面上。除了用户界面之外,其他东西经常将数据导入、更新或从数据库中删除,包括导入、批量更新以更改定价方案、热修复等。如果您需要确保规则始终得到遵守,请将逻辑放在默认值和触发器中。

这并不是说,在用户界面中也没有这个好主意(为什么要发送数据库不接受的信息),但忽略这些数据库中的事情就是在冒着灾难的风险。


7

虽然在应用层上有业务逻辑的好处,但我想指出的是,与数据库相比,编程语言和框架似乎更加经常更换。

我支持的一些系统在过去10-15年中经历了以下UI界面:Oracle Forms/Visual Basic/Perl CGI/ASP/Java Servlet。唯一没有改变的是关系型数据库和存储过程。


4
好观点;然而这可能只是因为更改数据库管理系统(DBMS)非常痛苦,而这其中一个主要原因是使用供应商特定语言中的存储过程(sprocs)。 - sleske

6
唯一应该存入数据库的是数据。
存储过程是维护的噩梦,它们不是数据,也不属于数据库。开发人员和DBA(数据库管理员)之间的不断协调只会导致组织内部的摩擦。
很难对存储过程进行良好的版本控制。数据库外部的代码非常容易安装——当你认为你拥有错误的版本时,你只需要执行“SVN UP”(可能需要安装),你的应用程序就回到了一个已知的状态。你可以使用环境变量、目录链接以及大量的环境控制来控制应用程序。
通过简单的路径操作,你可以针对不同的情况(培训、测试、QA、生产、客户特定增强等等)有不同的软件可用。
然而,数据库内部的代码要难得多。没有适当的环境——没有“PATH”、目录链接或其他环境变量——提供任何可用的控制,无法确定正在使用哪个软件;你必须使用永久的、全局绑定的应用程序集,与数据绑定在一起。
触发器甚至更糟。它们既是维护问题,也是调试问题��我不明白它们解决了什么问题;它们似乎是解决了那些无法正确使用现有类(或函数库)的糟糕设计应用程序的方法。
虽然有些人认为性能方面的论点很有说服力,但我还没有看到足够的基准数据来使我相信存储过程非常快。每个人都有一个轶事,但没有人有算法大致相同的并排代码。
[在我看到的例子中,旧应用程序是一个糟糕的设计混乱,当存储过程被编写时,应用程序被重新架构。我认为设计变化比平台变化更具影响力。]

1
“很难对存储过程进行良好的版本控制。”一个像Oracle的SQL Developer这样的体面的数据库开发工具将与源系统控制集成。 我同意触发器 - 除了审计之外,我讨厌它们。 - David Aldridge
不进行处理,不改变状态,也不涉及业务逻辑。它们是定义性项或约束条件,而非处理项。规则是:不改变状态。任何类似赋值语句的操作都属于处理。 - S.Lott
SP的价值在于最佳地打包功能。这种最佳实现可以用Java来完成。进行一些基准测试,你会发现如果你打包得好,Java可以胜过PL/SQL。 - S.Lott
2
我只能说,你们的组织在数据库管理系统开发方面的技能很不称职。你们已经把正确使用数据库管理系统所带来的一些潜在好处变成了负担。 - dkretz
1
@annakata:“数据库中的其他所有内容都有其存在的目的”——有时候这个目的是“因为很酷”或者“我们的竞争对手也有它”。在存储过程被发明之前,我就已经使用关系型数据库管理系统了(我很老)。当时目的不清楚,现在仍然不清楚。 - S.Lott
显示剩余6条评论

6
数据库独立性是将逻辑从数据库中抽出来的最有力的论据,但在此情况下,提问者排除了这一考虑。实现数据库独立性的最有力论据是能够向拥有自己数据库后端偏好的公司销售软件。
因此,我认为将存储过程从数据库中移出的主要论据只是商业上的考虑,而非技术上的考虑。虽然可能存在技术原因,但保留存储过程也有技术原因,例如性能、完整性和允许多个应用程序使用相同API等。
是否使用存储过程也受到将要使用的数据库的强烈影响。如果不考虑数据库独立性,则使用T-SQL或PL/SQL会有非常不同的体验。
如果您正在使用Oracle开发应用程序,则PL/SQL是一种明显的语言选择。它与数据紧密耦合,在每个版本中不断改进,并且任何像CVS或Subversion这样的良好开发工具都将集成PL/SQL开发。
甚至Oracle的基于Web的Application Express开发环境也完全由PL/SQL构建。

在Java中编写存储过程怎么样?这样不会减轻对DBMS的依赖问题吗? - sleske
1
@sleske,如果你要转向Sql Server,这将不是问题 :) - EightyOne Unite

5
现在,您可以将存储过程代码提交到Subversion,并使用良好的工具支持调试此代码。
如果使用组合SQL语句的存储过程,则可以减少应用程序和数据库之间的数据流量,减少数据库调用次数并获得大量性能提升。
一旦我们开始使用C#构建,我们决定不使用存储过程,但现在我们正在将越来越多的代码移动到存储过程中。特别是批处理。
但请不要使用触发器,而是使用存储过程或更好的包。触发器会降低可维护性。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接