知识卡片

数据库是业务逻辑间接使用的工具,边界线应画在依赖箭头指向业务逻辑之处

结构图卡

内容

边界线该画在互不相关的事情中间:GUI与业务逻辑无关、数据库与GUI无关、数据库与业务逻辑同样无关——最后一条常常引发争议,很多人已经习惯把数据库当成业务逻辑不可分割的一部分,但这个直觉是错的。数据库应该是业务逻辑间接使用的一个工具,业务逻辑不需要了解表结构、查询语言或任何数据库内部实现细节,它唯一需要知道的,是一组可以用来查询和保存数据的函数——数据库因此可以被隐藏在接口背后:BusinessRules通过DatabaseInterface加载保存数据,DatabaseAccess负责实现这个接口、并与真实的Database交互。边界线的具体位置很关键:它要画在穿过继承关系、在DatabaseInterface之下的地方,DatabaseAccess类对外的两个箭头都指向远离自己的方向,意味着它指向的类完全不知道DatabaseAccess的存在。把视角拉高到组件层面,Database组件的箭头指向BusinessRules组件,而反过来不成立——这意味着Database组件知道BusinessRules组件的存在(因为它内部负责把BusinessRules的函数调用转译成具体的数据库查询语句,这层转换代码必须了解BusinessRules),但BusinessRules组件完全不知道Database组件的存在。这个不对称箭头带来的实际收益是:Database组件不会对BusinessRules组件形成干扰,但Database组件离不开BusinessRules组件而独立存在;BusinessRules因此可以随意搭配Oracle、MySQL、CouchDB、Datomic甚至大文件这些不同的Database实现,业务逻辑压根不必关心具体用了哪个——这正是为什么与数据库相关的决策可以被安心推迟,先专注写业务逻辑代码并测试,直到不得不选数据库为止。

结构图

flowchart LR
    subgraph BusinessRules组件
    BR[BusinessRules类] --> DI["DatabaseInterface(接口)"]
    end
    subgraph Database组件
    DA[DatabaseAccess类] -.实现.-> DI
    DA --> DB[(具体Database实现<br/>可替换: Oracle/MySQL/文件等)]
    end

参考来源

- 位置:《架构整洁之道》第17章《划分边界》"应在何时、何处画这些线"(源文件:_epub-src/text/part0014_split_002.html) - 结论依据:原文说明数据库是业务逻辑间接使用的工具、边界应画在DatabaseInterface之下,并明确Database组件依赖BusinessRules组件(箭头指向BusinessRules)而非反过来,使得业务逻辑可以搭配任意数据库实现且相关决策可被推迟,直接支撑本卡片的结构图与解释。 - 原始内容:业务逻辑并不需要了解数据库的表结构、查询语言或其他任何数据库内部的实现细节……Database组件不会对BusinessRules组件形成干扰,但Database组件却不能脱离BusinessRules组件而存在……数据库可以用Oracle、MySQL、Couch或者Datomic,甚至大文件来实现。业务逻辑并不需要关心这件事。