数据库设计 Step by Step (6) —— 提取业务规则

阅读原文 转自博客园 知行思新

引言:数据库设计 Step by Step (5)中我们通过多种方法来理解客户的需求并撰写了需求文档。本文我们将回答三个问题。1. 为什么业务规则非常重要。2. 怎样识别业务规则。3. 如何修改关系模型并隔离出业务规则。

什么是业务规则

业务规则描述了业务过程中重要的且值得记录的对象、关系和活动。其中包括业务操作中的流程、规范与策略。业务规则保证了业务能满足其目标和义务。

生活中的一些业务规则可能是:

当顾客进入店内,最近的员工须向顾客打招呼说:“欢迎来到×××”。
当客户兑换超过200元的奖券时,柜员须要求查看客户的身份证并复印。当兑换的奖券金额小于25元时,无需客户签字。
早上第一个进办公室的人需要把饮水机加热按钮打开。
本系列我们关注数据库相关的业务规则,一些例子如下:

只有当客户产生第一个订单时才创建该客户的记录。
若一名学生没有选任何一门课程,把他的状态字段设为Inactive。
若销售员在一个月中卖出10套沙发,奖励500元。
一个联系人必须至少有1个电话号码和1个email邮箱。
若一个订单的除税总额超过1000元则能有5%的折扣。
若一个订单的除税总额超过500元则免运费。
员工购买本公司商品能有5%的折扣。
若仓库中某货品的存量低于上月卖出的总量时,则需要进货。
从数据库的视角来看,业务规则是一种约束。简单的约束如:

所有订单必须有一个联系电话。

上述这类简单的规则可以很容易的映射到关系数据库定义中,为字段确定数据类型或设定某字段为必填(不能为NULL)。某些业务规则表达的约束会复杂些,如:

学生每天的上课时间加上项目时间必须在1至14小时之间。

我们可以通过check约束或外键约束来实现这类业务规则。对于一些非常复杂的业务规则,如:

一名教员每周不能少于30小时工作量,其中分为办公时间、实验时间和上课时间。每1小时的课需要0.5小时办公时间进行备课。每1小时实验需1小时办公准备。每周指导学生论文时间不少于2小时。

类似上述的业务规则需要从多个表中收集数据,故在程序代码中实现最为合适。

识别关键业务规则

记录所有的业务规则并对这些规则进行分类能帮助我们更好的在系统中实现业务逻辑

如何实现业务规则不仅与当前的业务逻辑有关,而且与该业务逻辑将来如何变化有关。当一个规则在将来很可能变化时,我们需要使用更复杂但更灵活的方式构建该规则。

举例来说,假设公司只能向当地设有仓库的城市发货,这些城市包括:南京、长沙、西安、广州。业务规则要求订单中的发货城市字段必须为NJ、CS、XA、GZ之一。

我们可以把该规则简单的实现为check约束。但将来公司若在上海有了一个新仓库,就必须从后台数据库端修改该check约束。若公司随后设立更多新仓库或业务规则变化为可以向没有仓库的城市发货,每次我们都需要修改该约束。

考虑另一种实现该业务规则的方法——使用外键。我们创建一张ShippingCities表,其中存放值:NJ、CS、XA、GZ,并让订单表中的发货城市字段外键引用ShippingCities表中的主键。这样订单的发货城市列只能接受ShippingCities中存在的城市。当支持的发货城市增加或减少时,只需要在ShippingCities中插入或删除记录。

两种方式的实现难度差异不大,但前一种方式每次都需要修改数据库结构,后一种只需要修改数据。修改数据不仅更省力而且技术要求也更低。

上述业务规则实现为check约束可能如下:

ShippingCity = ‘NJ’ or ShippingCity = ‘CS’ or ShippingCity = ‘XA’ or ShippingCity = ‘GZ’

上述代码并不复杂,但只有熟悉数据库的程序员从后台才能修改。ShippingCitis表中的数据相对更易于理解,我们可以提供一个界面来让用户自己维护其中的城市。

要识别关键业务规则,我们可以问自己两个问题。

第一、修改规则会有多困难。越是复杂的规则,修改起来越困难且更容易出错。

第二、规则变化的可能性有多大。变化频繁的规则需要额外的设计来更好的应对将来的变化。

需要特别注意的规则(关键业务规则):

枚举值。例如:有效的发货城市,订单状态(Pending, Approved, Shipped)等。
计算参数。例如:对500元以上的订单免运费。这一数值可能在将来会调整为300元或600元。
有效参数。例如:项目组可由2至5人组成。某些项目是否可能由1个人完成或有更多人参与。
交叉记录和交叉表检查。例如:订单中可订购的货品数量不能超过该货品的当前库存数。
可概括性约束。如果可预见到将来需应用一些类似的约束,我们可以考虑把这些约束抽象出来进行管理。例如:某保险公司最近主推保险产品A。对每月能卖出20份A产品的销售人员给予1000元奖金。对于不同的保险产品在不同的时间段可能有不同的推广奖励规则。我们可以把产品名称、编号、销售量、奖金数额、促销时间段提取出来放到一张独立的表中作为计算奖金的参数。
非常复杂的检查有些检查规则非常复杂,把这些规则放到程序代码中实现更为容易和清晰。例如:学生选择理学院的谓词演算课程的前提是已通过理学院的命题演算课程或已通过社科院的逻辑I和II课程或者需要导师的允许。该规则在某些数据库产品中可以通过表级的check约束实现,但放到程序中更易于维护和理解。
一些直接可以在数据库中实现的业务规则:

固定枚举值。例如:性别(男、女),用手习惯(左撇子、右撇子)。
数据类型要求。每个字段具有确定的数据类型是关系型数据库的重要特性之一。滥用通用的数据类型(如string)对性能和数据防错都会带来损害。
必填值。例如:会员必须有手机联系方式。
合理性检查。合理性检查设定的范围基本不会变化。例如:商品的价格大于等于0。
作为软件从业人员不要拒绝或回避变化。世界上唯一不变的就是变化。在收集业务规则时多去了解该规则的业务背景与历史变化历程,而不是逼迫客户保证规则不会变化。尽可能发现所有的业务规则并记录下来。对这些业务规则按变化的可能性和修改难度进行分类,精心设计那些将来可能变化且修改困难的规则。

提取关键业务规则

识别并分类业务规则之后,我们需要在数据库中或数据库外来实现关键业务规则。我们可以参考如下方法:

1. 若规则为检验一组有效值时,把该规则转化为外键约束。先前举例中的有效发货城市就是一个很好的例子。创建ShippingCities表,填入允许的发货城市。然后把Orders表的ShippingCity列设为外键,引用ShippingCities表的主键。

2. 若规则为参数可能变化的计算式时,把这些参数提取到一张表中。例如:一个月内卖出总价超过100万元汽车的销售员能获得500元奖金。把参数100万元和500元提取到一张表中,如果需要甚至可以把一个月的时间段也作为参数提取出来。

我还见过一些软件系统在数据库中有一张通用的参数表。该通用参数表中存放系统需要的各种参数,一些是用于计算、一些是作为检验、另一些决定系统的行为。每一条记录有两个字段:Name和Value。例如需要确定一名销售员能获得多少奖金,我们先要查找Name字段为BonusSales的记录,检查该销售员的销售额是否达到了Value字段的金额,若答案是肯定的再查找Name字段为BonusAward的记录来确定奖金数额。这种设计另有一好处,在程序启动时可以把通用参数表读入内存的某集合中,此后使用参数值时就无需再次连接数据库。

3. 若逻辑或计算规则很复杂时,则提取到代码中进行实现。这里说的代码可以是应用程序端代码,还可以是数据库端存储过程。把规则放到代码中实现的意义在于业务规则与数据库表结构分离了,规则的变化不会影响到数据库表结构。通过结构化编程或面向对象编程来实现复杂的规则更易于维护。

举一个综合性的例子:

一本关于数据库设计的书籍卖出前5000本的版税为5%,5000本至10000本之间的版税为7%,超过10000本后的版税为10%,不同类型书籍的版税可能不同。

上述规则比较复杂且包含多个可能变化的参数,故使用第1、2条方法。我们可以通过存储过程来实现该规则,并把参数隔离到一张参数表中进行维护。创建的参数表为RoyaltyRates,并通过BookId与Books关联(如图1所示)。这样为不同书籍创建新的版税规则就非常容易了。

图1 参数表RoyaltyRates与Books表的关系

多层应用的概念大家都不会陌生。三层应用是最常见的分层方法。对于复杂的业务逻辑一般会在中间层(即业务层)中实现。对于一些基本的验证,如必填信息、数字有效区间等,需要在最上层用户界面以及最底层数据库端进行双重检验。数据库端的约束是阻隔脏数据进入系统的最后一道防线,而用户界面处的检验可以避免错误数据传输到系统后端才被拒绝,节省了系统资源。

注:关于多层应用的更多资料请参见最后的“总结与参考”部分。

Summary

主要内容回顾

1. 业务规则决定了业务如何运行,其涵盖从简单明了的入门打卡到复杂的奖金计算公式。

2. 对于数据库而言,业务规则将影响到数据模型。业务规则确定了每个字段的域(值的类型和范围),是否是必须的,以及该字段要满足的其他条件。

3. 理解业务规则并识别那些需要特别处理的关键规则至关重要。

4. 有些规则简单且基本不变,它们可以很容易的用数据库特性来实现。其他的一些规则可能复杂或时常变化,我们可以把它们从数据库中逻辑的或物理的隔离出来(隔离到参数表、存储过程或业务层中),使它们易于修改。

多层应用参考

1. 谈谈对于企业级系统架构的理解(http://www.cnblogs.com/liping13599168/archive/2011/05/11/2043127.html)

2. Multitier architecture(http://en.wikipedia.org/wiki/Multitier_architecture)

3. Software Architecture, Architects and Architecting(http://www.bredemeyer.com/)

数据库设计Step by Step (7)——概念数据建模

发表回复