知识卡片
数据库是业务逻辑间接使用的工具,边界线应画在依赖箭头指向业务逻辑之处
内容
边界线该画在互不相关的事情中间: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