公司的产品最近开始向 CRM 产品转型,组织上决定设计一个新的业务权限描述模型。

业务权限描述模型是为了解决「是否允许谁对什么做什么」而设计的存储结构。首先,在满足范式的前提下很容易设计出一个最小的权限描述模型

Naive Design

最小模型支持存取每个用户对 object 的操作权限(通过添加和删除 can_handle_object 记录)满足了权限管理的基本需求。

第二步,管理更多实体。假设这是一家连锁超市仓储管理系统的数据库,那么 object 可能是 productdepot 或者 store。比如在语境「“修改用户权限”的权限」里 objectuser 实体。

比较正确的姿势是采用类似“继承”的做法

Simple Design

p_node 意思是 permission node。考虑实际语义的话,称为 pctl_node 会更好。

外键 p_node_id 相当于面向对象语言实现上的基类指针,因此可以通过 can_handle_object 查询任何含有 p_node_id 的实体的权限。PostgreSQL 的 INHERITS 语法也是相似的原理。

新模型可以描述不同实体的权限,权限的管理却依旧非常困难 —— 假设 userpermissionp_node 数量分别为

  • 添加一个 user 需要考虑 个权限关系
  • 添加一个 permission 需要考虑 个权限关系
  • 添加一个 p_node 需要考虑 个权限关系

不难看出,虽然可以从最小的粒度进行最精细的权限管理,但是无论就性能还是可用性而言目前的设计都是不可接受的。

改进方法是增加层级,比如引入 permission_group 实体

EER Improved

个权限表示为 permission_group 则原本 的记录规模就能缩减到 。使用类似的思路创建 user_group 实体也可以减小 对应的规模。

在更复杂的需求中,可能发生几个 permission_group 包含相同 permission 或者类似的情况。设计权限描述模型的时候就需要使用 m:n 来表示“组”和实体的关系。

最后,当然不会忘了给 p_node 增加层级,p_node 的数量可比前两者多的多的多,因此这是个设计时必须考虑的方案。有了先前的经验很容易想到添加一个 p_node_group,但更高效的做法是给 p_node 添加一个到自己的 1:n 关系

EER Fancy Partial

新模型相当于同时定义了

  • p_node
  • p_node_group
  • group_of_p_node_group
  • group_of_group_of_p_node_group

那么 p_node 可以有任意多层!每个 p_node 都可以关联到一个父节点,形成一个树状结构。和某个节点建立关联就可以表示用户到该节点和所有子节点的权限。

EER Fancy

这个结构需要递归查询,得到每一级 p_node_id 之后连接 can_handle_object 及其相关表就能得到权限结果。

综上所述,权限描述是一个对 m:n:k 关系(谁/对什么/做什么)的建模问题,不同的权限管理需求决定不同的组织形式:

  • 不分层,如 user
  • 目录式(1:n)
    • 单层,如 permission
    • 多层,如 p_node
  • 标签式(m:n)
    • 单层,如 Unix 用户/组
    • 多层

除了权限描述模型,相同的模式也适用建模优惠券业务和其他多对多对多关系的建模。