数据库应实现多少业务逻辑?


107

我参与过一些项目,其中大多数业务逻辑是在数据库上实现的(主要是通过存储过程)。另一方面,我从其他程序员那里听说这是一个不好的做法(“那里有数据库来存储数据。其余的则由应用程序来完成”)。

通常,哪种方法更好?

我能想到的在数据库中实现业务逻辑的优点是:

  • 集中业务逻辑;
  • 应用程序类型,编程语言,操作系统等的独立性;
  • 数据库不太容易进行技术移植或大型重构(AFAIK);
  • 无需重新进行应用程序技术迁移(例如:.NET到Java,Perl到Python等)。

缺点:

  • 由于缺乏面向应用程序的语言所提供的库和语言构造,SQL对于业务逻辑编程而言效率较低,并且更为复杂。
  • 通过库更难(如果可能的话)重用代码;
  • 生产效率较低的IDE。

注意:我正在谈论的数据库是关系型,流行的数据库,例如SQL Server,Oracle,MySql等。

谢谢!


3
您可能会发现此问题的答案很有用。
Blrfl 2013年

7
这个论点已经被详尽讨论过了。我们还能在这里有意义地添加更多内容吗?
罗伯特·哈维

2
@gnat:甚至没有。
罗伯特·哈维


7
考虑数据库将远远超出应用程序的寿命。数据库甚至可能超过了您编写应用程序所使用的语言。数据本身通常业务,数据库应该能够保护其中包含的数据的完整性。因此,坦率地说,每个外键约束都是业务规则的实施。除非您摆脱了关系数据库中的所有关系约束,否则您实际上无法完全从数据库中摆脱业务逻辑。
Craig 2015年

Answers:


82

业务逻辑不会进入数据库

如果我们谈论的是多层应用程序,那么很显然,业务逻辑(一种运行特定企业的智能)属于业务逻辑层,而不是数据访问层。

数据库确实做得很好:

  1. 他们存储和检索数据
  2. 他们在不同的数据实体之间建立和执行关系
  3. 它们提供了查询数据以获取答案的方法
  4. 他们提供性能优化。
  5. 他们提供访问控制

现在,您当然可以在数据库中整理与业务相关的各种事物,例如税率,折扣,操作代码,类别等。但是由于其他人已经提到的各种原因,对数据执行的业务操作通常不会编码到数据库中,尽管可以在数据库中选择操作并在其他位置执行。

当然,出于性能和其他原因,可能会在数据库中执行某些操作:

  1. 结算会计期间
  2. 数字运算
  3. 每晚批处理
  4. 故障转移

自然,什么都没有刻在石头上。存储过程仅因为它们驻留在数据库服务器上并具有一定的优势和优势,所以适合于多种任务。

存储过程无处不在

在存储过程中对所有数据存储,管理和检索任务进行编码有一定的诱惑力,并且仅使用生成的数据服务即可。当然,您将从数据库服务器可以提供的最大性能和安全性优化中受益,这并非易事。

但是你有什么风险?

  1. 供应商锁定
  2. 对具有特殊技能的开发人员的需求
  3. 整体Spartan编程工具
  4. 极其紧密的软件耦合
  5. 不必分离关注点

当然,如果您需要Web服务(无论如何,这可能都是标题),您仍然必须构建它。

那么什么是典型做法?

我会说一种典型的现代方法是使用对象关系映射器(例如Entity Framework)来创建对表进行建模的类。然后,您可以通过返回对象集合的存储库与数据库对话,任何有能力的软件开发人员都非常熟悉这种情况。ORM动态生成与您的数据模型和所请求的信息相对应的SQL,然后数据库服务器对其进行处理以返回查询结果。

这项工作效果如何?很好,并且比编写存储过程和视图要快得多。通常,这可以满足大约80%的数据访问需求,其中大部分是CRUD。其余的20%覆盖了什么?您猜对了:存储过程,所有主要的ORM都直接支持该存储过程。

您可以编写一个与ORM相同但具有存储过程的代码生成器吗?你当然可以。但是ORM通常独立于供应商,每个人都很好理解并且得到了更好的支持。


3
谢谢您的出色回答,@ Robert Harvey。但是我在考虑“供应商锁定”的论点:是否不使用特定技术(例如.NET或Java堆栈)来构建应用程序,同时还是供应商锁定?还是面向应用程序的堆栈供应商锁定与数据库锁定相比有优势吗?
拉斐尔

3
@RobertHarvey,但是.NET中的应用程序逻辑部分仍然锁定在.NET中。PHP和Java也是如此。
Pacerier 2014年

2
@Pacerier:通过供应商锁定,我指的是数据库供应商。实际上,很少替换数据库(和编程堆栈)。
罗伯特·哈维

2
@kai:好吧,你不能两全其美。您要么使用存根和模拟并接受测试是人为的事实,要么编写真实的测试并稍作延迟。我怀疑您的权衡是10分钟而不是30秒。
罗伯特·哈维

3
也许是晚了,但我认为实现业务逻辑的存储过程属于业务逻辑层,而不是数据层。它们是一种独立的lang,不需要ORM。
生命在线

16

我坚信尽可能将业务逻辑排除在数据库之外。但是,作为我公司的绩效开发人员,我赞赏有时有必要取得良好的绩效。但是我认为这比人们声称的要少得多。

我对您的利弊有异议。

您声称它集中了您的业务逻辑。相反,我认为它分散了权力。在我目前正在研究的产品中,我们将存储过程用于许多业务逻辑。我们的许多性能问题来自反复调用函数。例如

select <whatever>
from group g
where fn_invoker_has_access_to_group(g.group_id)

这种方法的问题是通常(在某些情况下可能是错误的)将数据库强制运行N次(每行一次)。有时,该功能很昂贵。一些数据库支持功能索引。但是您无法针对每个可能的输入为每个可能的函数建立索引。可以吗

上述问题的常见解决方案是从函数中提取逻辑并将其合并到查询中。现在,您已经破坏了封装和重复的逻辑。

我看到的另一个问题是在循环中调用存储过程,因为没有办法连接或相交存储的proc结果集。

declare some_cursor
while some_cursor has rows
    exec some_other_proc
end

如果您从嵌套proc中提取代码,那么您将再次去中心化。因此,您不得不在封装和性能之间进行选择。

总的来说,我发现数据库不擅长:

  1. 计算方式
  2. 迭代(它们针对设置操作进行了优化)
  3. 负载均衡
  4. 解析中

数据库擅长:

  1. 锁定和解锁
  2. 维护数据及其关系
  3. 确保诚信

通过执行昂贵的操作(例如循环和字符串解析)并将其保留在您的应用层中,您可以水平扩展应用程序以获得更好的性能。在负载均衡器后面添加多个应用服务器通常比设置数据库复制便宜得多。

但是,您是对的,它使您的业务逻辑与应用程序的编程语言脱钩,但是我不明白为什么这是一个优势。如果您有Java应用程序,则您有Java应用程序。将一堆Java代码转换为存储过程不会改变您拥有Java应用程序的事实。

我的首选是使数据库代码专注于持久性。如何创建新的小部件?您必须插入3个表中并且它们必须处于事务中。那属于存储过程。

定义对窗口小部件可以执行的操作以及查找窗口小部件的业务规则属于您的应用程序。


8
在SQL Server中,只需要在循环中调用写得不好的sps,就可以在参数中向其发送数据集并执行基于集的过程。
HLGEM 2013年

2
只要WHERE子句中有UDF,SQL Server就会生成次优查询计划。
Jim G.

7
看起来您的性能问题不是数据库与应用程序中逻辑的错。它只是编写和架构欠佳。在ORM世界中,同样的问题也会跟着您。在CRUD操作之外,ORM可能是一个令人头疼的问题。如果您的系统数据量大,报告系统类型,请谨慎使用。
Sam Yi

那是真实的。我们大多数的性能问题仅是由于编写不良的代码和过于复杂的体系结构所致。但是我仍然相信我们在数据库中输入了错误的工作类型。尽可能多地将代码编码到数据库中已导致我们做数据库不擅长的事情。
布兰登2014年

1
这个示例甚至是将biz逻辑的核心部分放置在数据库中的一个论据:避免像瘟疫这样的迭代方法(代码或游标循环而不是基于集合的表达式)。程序员倾向于以迭代方式(循环,遍历)处理对象集,这可能导致不必要的负载或许多单查询往返的SELECT N + 1问题。通过使用SQL或基于语言的表达式(例如LINQ),将尽可能地迫使它们使用基于集合的方法。
埃里克·哈特

10

我曾在两家对此主题有不同见解的公司工作。

我个人的建议是在执行时间很重要(性能)时使用存储过程。由于存储过程是已编译的,因此如果您有复杂的逻辑来查询数据,则最好将其保留在数据库本身上。同样,它只会在最后将最终数据发送到您的程序。

否则,我认为程序的逻辑应该始终在软件本身中。为什么?因为程序需要是可测试的,所以我认为没有简单的方法可以对存储过程进行单元测试。不要忘记,未经测试的程序是错误的程序。

因此,在需要时请谨慎使用存储过程。


3
存储过程可以进行单元测试。有关某些技术,请参见此处
罗伯特·哈维

4
afaik,单元测试永远不要使用数据库或文件。因此,从技术上讲,“单元测试”存储过程不是单元测试,它会变得很慢。在开发过程中的任何时候,单元测试套件都应在几秒钟内运行(对于大型应用程序,则可能需要数分钟)。
让-弗朗索瓦的Côté

1
OP正在谈论“业务逻辑”,并且应该对业务逻辑进行单元测试。通过将其放入存储过程中,可以将其与数据库查询混合使用,这会减慢整个过程。就像我说的那样,您可以使用存储过程(这不是犯罪),但是它将模糊业务逻辑和数据库层之间的界限,这是很糟糕的。小心:)使用它
让-弗朗索瓦的Côté

1
如果创建数据库和必要的对象,则先进行sp,测试,然后再将其拆除,这是一个单元测试。它测试一个工作单元。
Tony Hopkinson

2
存储过程神话带来的性能提升是否被揭穿了?
JeffO

9

您需要找到一个中间立场。我见过可怕的项目,程序员在其中使用数据库的不过是价格过高的键/值存储。我见过其他程序员无法使用外键和索引的地方。在另一方面,我看到了一些项目,其中大多数(如果不是全部)业务逻辑都在数据库代码中实现。

正如您已经指出的,T-SQL(或其他流行的RDBMS中的同等功能)并不是编码复杂业务逻辑的最佳场所。

我试图建立一个合理的数据模型,使用数据库的功能来保护我对该模型的假设(即FK和约束),并少量使用数据库代码。当您需要数据库非常擅长的某些事情(例如,总和)时,数据库代码很有用;当您不需要这些代码时,可以避免在网络上移动大量记录。


2
正如NoSQL从业人员所证明的那样,将数据库用作“定价过高”的键/值存储是一种完全有效的技术。
罗伯特·哈维

1
@RobertHarvey您显然是正确的,但是如果您只需要键/值存储,那么我的直觉仍然坚持必须有比数据库更简单/更便宜/更快的解决方案。我需要了解有关NoSQL的更多信息。
Dan Pichelman 2013年

2
我不认为使用存储过程来解决设计不良的数据库。
JeffO

2
@RobertHarvey,我从字面上读了“定价过高的键/值存储库”。当有诸如MongoDB之类的选项免费提供时,为此支付Oracle或SQL Server许可就好像在浪费钱。
拉斐尔2013年

@Raphael或者你可以使用PostgreSQL😉
黛咪

9

如果您的业务逻辑涉及集合操作,那么数据库中最适合的地方是数据库,因为数据库系统确实擅长执行集合操作。

http://en.wikipedia.org/wiki/Set_operations_(SQL)

如果业务逻辑涉及某种计算,则它可能属于数据库/存储过程之外,因为数据库并不是真正为循环和计算而设计的。

尽管这些规则不是一成不变的,但它是一个很好的起点。


6

没有一个正确的答案。这取决于您使用数据库的目的。在企业应用程序中,您需要通过外键,约束,触发器等在数据库中提供逻辑,因为它是所有可能的应用程序共享代码的唯一位置。此外,将所需的逻辑放入代码中通常意味着数据库不一致并且数据质量较差。对于仅同意GUI工作原理的应用程序开发人员来说,这似乎微不足道,但是我向您保证,尝试在合规性报告中使用数据的人们会发现,当他们因拥有不符合标准的数据而被罚款十亿美元时,这将非常烦人且代价高昂。没有正确遵守规则。

在非监管环境中,当您不太在意整个记录集,而只有一个或两个应用程序访问数据库时,也许您可​​以将所有内容保留在应用程序中。


3

几年后,这个问题仍然很重要。

对我而言,简单的经验法则:如果它是逻辑约束或无处不在的表达式(单个语句),请将其放置在数据库中(是的,外键和检查约束也是业务逻辑!)。如果是程序性的,则通过包含循环和条件分支(实际上不能将其更改为表达式),将其放入代码中。

避免垃圾堆数据库

试图将所有业务逻辑真正放入应用程序代码中,很可能会将(关系)数据库退化为垃圾堆,其中关系设计大部分被完全省略,数据可能具有任何不一致的状态,并且规范化缺失(通常主要是XML,JSON) ,CSV等垃圾桶列)。

这种仅用于应用程序的逻辑可能是NoSQL崛起的主要原因之一-当然,不利的一面是应用程序必须照顾所有逻辑本身,而数十年来已内置在关系数据库中。但是,NoSQL数据库更适合此类数据处理,例如,数据文档在其内部维护隐式的“关系完整性”。对于关系数据库,它只是滥用,造成了更多的麻烦。

表达式(基于集合)而不是过程代码

在最佳情况下,每个数据查询或操作都应编码为表达式,而不是过程代码。当编程语言支持表达式(例如.NET世界中的LINQ)时(对此,很遗憾,目前仅查询,没有任何操作)。在关系数据库方面,已经有很长的一段时间被教导说,它比过程游标循环更喜欢SQL语句表达式。因此,数据库可以优化,并行执行操作或其他有用的操作。

利用数据库数据完整性机制

对于具有外键和检查约束的RDBMS,计算所得的列,可能的触发器和视图,这是在数据库中存储基本业务逻辑的地方。适当的规范化有助于维护数据完整性,以确保数据的唯一性和独特性。即使必须在代码和数据库中复制它,也不应忽略这些基本的数据完整性机制!

存储过程?

如今,存储过程几乎不再需要,因为数据库会保留已编译的SQL执行计划,并在再次出现同一查询时仅使用不同的参数来重用它们。因此,SP的预编译参数不再有效。一个人可以在应用程序或ORM中存储或自动生成SQL查询,这将在大多数时间找到预编译的查询计划。只要您不明确使用过程元素,SQL就是一种表达语言。因此,在最佳情况下,您使用可以转换为SQL的代码表达式。

尽管与存储过程不同,应用程序端(包括由ORM生成的SQL)不再位于数据库内部,但我仍将其视为数据库代码。因为它仍然需要SQL和数据库知识(最简单的CRUD除外),并且如果正确应用,其工作方式将与通常使用C#或Java这样的编程语言创建的过程代码大不相同。


2

它的确取决于业务,其文化和遗产。除了技术上的考虑(双方都讨论过)之外,给出的答案还告诉您,它取决于人们的来源。在某些组织中,数据为王,而DBA是强大的人物。这是典型的集中式环境,即一个数据中心,其中连接了许多终端。在这种类型的环境中的偏好是显而易见的。在数据中心发生任何更改之前,台式机可能会进行许多次根本性的更改,而两者之间几乎没有什么变化。

另一端是纯3层架构。或者在面向Web的业务中是多层的。您可能会在这里听到一个不同的故事。DBA(如果有的话)只是执行一些管理任务的助手。

现代的应用程序开发人员将对第二种模型更具亲和力。如果您在大型客户端-服务器系统上长大,那么您可能会处于另一个阵营。

这里经常涉及到许多与非技术环境相关的因素,对此问题没有普遍的答案。


2

术语“业务逻辑”易于解释。在构建系统时,我们要确保数据库及其内容的完整性。第一步,应该有不同的用户访问权限。作为一个非常简单的示例,让我们考虑一个ATM应用程序。

要获得帐户余额,在适当的视图上进行选择应该可以。但是,要转移资金,您希望交易由存储过程封装。不允许业务逻辑直接更新贷方和借方金额表。

在此示例中,业务逻辑可以在请求转移之前检查余额,或者简单地为转移调用存储的proc并报告失败。恕我直言,在此示例中,业务逻辑应先行检查是否有足够的资金可用并且目标帐户存在,然后才调用转移资金。如果在初始步骤和存储的proc调用之间发生另一笔借记,则只有这样才会返回错误。


漂亮的例子和解释。
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.