第二十章 业务规则
如果我们要将应用程序分成业务规则和插件,我们最好把握好实际的业务规则是什么。这有几种不同的类型。
严格地说,业务规则是制定或节省商业资金的规则或程序。非常严格地说,这些规则可以制造或节省商业资金,而不管它们是否在计算机上执行。即使手动执行,他们也会赚钱或省钱。
银行收取贷款N%利息是使银行赚钱的业务规则。也就是说一个计算机程序计算利息,还是职员用一个算盘计算利息,这并不重要。
我们将这些规则称为关键业务规则(Critical Business Rules),因为它们对于业务本身是至关重要的,即使没有系统使其自动化,也会存在。
关键业务规则通常需要一些数据来处理。例如,我们的贷款需要贷款余额,利率和付款时间表。
我们将这些数据称为关键业务数据(Critical Business Data)。这是即使系统没有自动化也会存在的数据。
关键规则和关键数据是密不可分的,所以它们对于对象的制定是一个很好的候选者。我们将这种对象称为实体(Entity)。
实体
实体是我们的计算机系统中的对象,体现了在关键业务数据上运行的一小组关键业务规则。实体对象包含关键业务数据,或者其非常容易访问关键业务数据。实体的接口由实现关键业务规则的函数组成,且这些函数对这些数据进行操作。
例如,图20.1显示了我们的贷款实体可能看起来像UML中的一个类。它有三个关键业务数据,并在其界面上呈现三个相关的关键业务规则。
图20.1贷款实体作为UML中的一个类
当我们创建这样的类时,我们正在聚集实现了对业务至关重要的概念的软件,并将其与我们正在构建的自动化系统中的所有其他问题区分开来。这个类是独立的业务呈现。对于数据库,用户界面或第三方框架的选择毫无关系。它可以在任何系统中为业务提供服务,而不管系统是如何呈现的,数据是如何存储的,或者系统中的计算机是如何安置的。实体是纯粹的业务,没有别的了。
你们中有些人可能担心我把它叫做类。不担心。你不需要使用面向对象的语言来创建一个实体。所有这一切都只需要你将关键业务数据和关键业务规则绑定在一个单独的软件模块中。
用例
并非所有的业务规则都像实体一样纯粹。一些业务规则通过定义和约束自动化系统运行的方式来为业务创造或节省资金。这些规则不会在手动环境中使用,因为它们只作为自动化系统的一部分才有意义。
例如,想象银行职员用来创建新贷款的应用程序。银行可能决定不要求贷款职员提供贷款支付估算,直到他们首先收集并验证联系信息,并确保候选人的信用评分为500或更高。为此,银行可以指定系统将不进行到支付估算屏幕,直到联系信息屏幕已经填写和验证,并且信用评分已经被确认为大于500。
这是一个用例。用例描述了使用自动化系统的方式。它指定了由用户提供的输入,要返回给用户的输出以及产生该输出所涉及的处理步骤。用例描述了应用程序特定的业务规则,而不是实体内的关键业务规则。
图20.2显示了一个用例的例子。请注意,最后一行提到了客户。这是对制定客户实体的参考,客户实体包含管理银行与客户之间关系的关键业务规则。
图20.2 用例例子
用例包含指定如何以及何时调用实体中的关键业务规则的规则。用例控制实体的动向(dance)。
还要注意,用例并没有描述用户界面,而是非正式地指定从该界面进入的数据,以及通过该界面返回的数据。从用例来看,不可能告诉应用程序是在Web上,在复杂客户端还是在控制台上,或者是纯服务。
这个非常重要。用例不描述系统如何显示给用户。相反,他们描述了管理用户和实体之间交互的应用程序特定规则。数据如何进出系统与用例无关。
用例是一个对象。它具有一个或多个实现应用程序特定业务规则的函数。它还包含数据元素,包括输入数据,输出数据和对其进行交互的相关实体的引用。
实体不知道控制它们的用例。这是依赖倒置原则之后依赖方向的另一个例子。诸如实体之类的高层概念对底层概念(如用例)一无所知。相反,较低层的用例知道更高层的实体。
为什么实体是高层级而用例是较低层级?因为用例是特定于单个应用程序的,因此更接近该系统的输入和输出。实体是可以在许多不同的应用中使用的泛化(generalizations),所以它们离系统的输入和输出更远。用例取决于实体;实体不依赖于用例。
请求与返回模型
用例期望输入数据,并产生输出数据。但是,格式良好的用例对象应该没有关于数据传递给用户或其他任何组件的方式的信息。我们当然不希望用例类中的代码知道HTML或SQL!
用例类为其输入接受简单的数据结构请求,并返回简单的响应数据结构作为其输出。这些数据结构不依赖于任何东西。它们不是从标准框架接口(如HttpRequest和HttpResponse)派生的。他们对网络一无所知,也不会分享任何用户界面可能存在的任何缺陷。
这种依赖关系的断开是至关重要的。如果请求和响应模型不是独立的,那么依赖它们的用例将间接绑定到模型携带的任何依赖项。1
你可能会想要让这些数据结构包含对实体对象的引用。你可能认为这是有道理的,因为实体和请求/响应模型共享这么多的数据。抵制这种诱惑!这两个对象的目的是非常不同的。随着时间的推移,他们会因为不同的理由而改变,所以以任何方式将他们联系在一起违反了“共同封闭”和“单一责任原则”。结果在你的代码中将是大量的流氓数据,大量的条件语句。
小结
业务规则是软件系统存在的原因。它们是核心功能。它们使得代码创造或节约资金。他们是传家宝。
业务规则应该保持原始状态,不受用户界面或使用的数据库等基础问题的影响。理想情况下,代表业务规则的代码应该是系统的核心,只需要关注较少的问题。2业务规则应该是系统中最独立和可重用的代码。
1. If the request and response models are not independent, then the use cases that depend on them will be indirectly bound to whatever dependencies the models carry with them. ↩
2. with lesser concerns being plugged in to them. ↩