20 Business Rules

若我们打算将应用拆分成业务规则与插件两部分,就必须先准确理解:业务规则究竟是什么?
事实证明,业务逻辑可分成好几种类型。严格来讲,业务规则是那些能为企业创造收益或者节省成本的规则或流程。更严苛地说,这类规则创造 / 节省收益的属性,与是否通过计算机实现无关 —— 即便纯靠人工执行,它们依然能产生同样的效果。

例如银行对贷款收取N%利息的规则,就越是一条能为银行创收的业务规则。

无论这笔利息是由计算机程序计算还是职员用算盘算出,都不影响这条规则和核心价值。我们把这类规则叫做核心业务规则。他们是企业核心的命脉,即使没有自动化系统这些规则本身也存在。

核心业务的执行通常需要一些数据,比如上述贷款的场景,就需要贷款余额,利率,还款计划等数据支撑。我们把这类数据称为核心业务数据。即使没有自动化系统这些数据也是企业运营必须的。

核心业务和核心数据密不可分,所以他们非常适合封装成一个对象,我们把这类对象称为实体。

Entities

image

实体是计算机系统中这样一种对象:它承载着一小部分作用于核心业务数据的核心业务规则

实体(Entity) 是计算机系统中这样一种对象:它承载着一小部分作用于 “核心业务数据” 的核心业务规则。实体对象要么直接包含核心业务数据,要么能极便捷地访问这些数据;实体的接口则由一系列函数构成,这些函数实现了作用于该数据的核心业务规则。
例如,图 20.1 展示了 “贷款(Loan)” 实体在 UML 中的类结构:它包含三项核心业务数据,并在接口层对外暴露了三条相关的核心业务规则。
图 20.1 贷款实体的 UML 类图
当我们创建这类类时,本质是把 “实现企业核心概念” 的代码归集到一起,并将其与自动化系统中的所有其他关注点隔离开。这个类作为企业核心概念的 “专属代表” 独立存在,完全不受数据库、用户界面、第三方框架等细节的污染。它可以在任意系统中为企业服务 —— 无论系统的展示形式、数据存储方式、硬件部署架构如何,都不影响它的核心价值。实体只聚焦纯业务逻辑,不掺杂任何其他内容。
或许有人会在意我把它称作 “类”—— 大可不必。创建实体并非必须使用面向对象语言,核心要求只有一个:将核心业务数据与核心业务规则绑定在一个独立的软件模块中。

Use Cases

image
并非所有业务规则都像实体那样纯粹。有些业务规则是通过定义和约束自动化系统的运行方式来为企业盈利或省钱的。这类规则不会用在人工环境里,因为它们只有作为自动化系统的一部分才有意义。
举个例子:设想一个银行职员用来创建新贷款的应用。银行可能规定:信贷员必须先收集并验证联系人信息,并且确认申请人的信用评分不低于 500 分,才能为其提供还款预估。因此,银行会要求系统必须在联系人信息页面填写并校验通过、且信用分数达标后,才能进入还款预估页面。
这就是一个用例(Use Case)。用例是对自动化系统使用方式的描述。它规定了由用户提供的输入、返回给用户的输出,以及生成该输出所涉及的处理步骤。用例描述的是特定于应用的业务规则,而不是实体内部的核心业务规则。
图 20.2 是一个用例示例。注意最后一行提到了 “客户”,它引用的是客户实体—— 其中包含管理银行与客户关系的核心业务规则。
用例包含的规则,用于规定如何、何时调用实体内部的核心业务规则。可以说,用例指挥着实体的行为流程。
同时要注意:用例并不会描述用户界面,只是非正式地指定从界面流入和流出的数据。仅凭用例,你完全无法判断这个应用是网页版、桌面客户端、控制台程序,还是纯服务接口。
这一点非常重要。用例不描述系统长什么样,而是描述控制用户与实体之间交互的、应用专属的规则。数据如何进出系统,与用例无关。
用例也是一种对象。它包含一个或多个实现应用特定业务规则的函数,同时也包含一些数据成员:输入数据、输出数据,以及对它所交互的相关实体的引用。
实体完全不知道控制它的用例存在。这又是一个依赖方向遵循依赖倒置原则的例子。像实体这样的高层概念,不依赖用例这类低层概念;反过来,低层的用例知道高层的实体。
为什么实体是高层,而用例是低层?因为用例只针对某一个应用,因此更靠近系统的输入和输出;而实体是通用的,可以在许多不同应用中复用,因此离系统的输入输出更远。
用例依赖实体,实体不依赖用例。

Request and Response Models

用例需要接收输入数据,并产生输出数据。但是,一个结构良好的用例对象,不应该知道这些数据是如何传递给用户或其他组件的。我们绝对不希望用例类内部的代码去感知 HTML 或者 SQL!
用例类只接收简单的请求数据结构作为输入,并返回简单的响应数据结构作为输出。这些数据结构不依赖任何外部技术。它们不会继承自框架标准接口,比如 HttpRequest 或 HttpResponse。它们对 Web 一无所知,也和任何用户界面无关。
这种无依赖的设计至关重要。如果请求和响应模型不是独立的,那么依赖它们的用例,就会被间接绑定到这些模型所携带的所有依赖上。
你可能会忍不住让这些数据结构直接引用实体对象。你可能觉得这很合理,因为实体和请求 / 响应模型共享大量数据。请务必抵制这种诱惑!
这两种对象的用途截然不同。随着时间推移,它们会因为完全不同的原因而发生变化。以任何形式将它们绑定在一起,都会违反共同封闭原则和单一职责原则。最终会导致代码中出现大量冗余传递数据和繁琐的条件判断

Conclusion

业务规则是一个软件系统存在的真正理由。它们是系统的核心功能,承载着为企业盈利或省钱的代码,是系统中最珍贵的 “传家之宝”。
业务规则应当保持纯粹,不受用户界面、数据库这类次要关注点的污染。理想情况下,表达业务规则的代码应该是系统的心脏,其他次要关注点都以插件形式接入。业务规则应当是整个系统中最独立、最可复用的代码。

核心思想

把系统中最值钱的核心业务规则(Entity)和系统使用规则(Use Case)拆干净,让他们不被UI,数据库,框架污染,同时用干净的DTO做数据传递,最终让核心逻辑独立,可复用,改不动

posted @ 2026-03-20 15:11  cyusouyiku  阅读(1)  评论(0)    收藏  举报