Provisa 中的业务逻辑¶
问题所在¶
每个组织都有业务逻辑的标准,大多数团队也都遵守。然而多年下来,随着系统不断建设和替换、团队不断更迭,逻辑分散到了 ETL 作业、仪表板、API 和存储过程中。每一块单独看都说得通。常常没有人规划过整体,更常见的是没有人跨越时间规划过整体。结果大家都很熟悉:“净收入”在三个地方有三种算法,没有人说得清哪个是对的。
行业对其中的部分问题已有答案。指标层常以语义层的名义出售,它把“净收入”定义一次,供每个工具读取。Open Data Product Standard(ODPS,开放数据产品标准)描述数据产品,Open Data Contract Standard(ODCS,开放数据契约标准)描述数据契约。Provisa 两者都能导出。这些都是好主意,也都越来越受欢迎,但每个只覆盖一部分。更大的问题是,它们如何作为一个连贯、可组合的模型融入受控、受治理的数据。派生或更改数据的逻辑需要与数据本身同样的治理:谁可以使用它,它读取和写入什么,它的结果去往哪里。
交付渠道让情况更糟。逻辑被分别构建进每个渠道:BI 工具里的计算字段、API 处理程序里的规则、应用解析器里的过滤条件。每个渠道最终都有自己版本的真相,因此同一个问题通过其中两个渠道提出,很少得到相同的答案。最终,人们放弃了:他们开始把这种混乱当作专业分工和服务众多不同利益相关方的代价来接受。
什么是业务逻辑¶
业务逻辑是创建、更新、删除或转换数据的规则。
业务术语是另一回事。域、表和列的名称与描述,以及本体中的术语,说明数据的含义。业务逻辑说明对数据做了什么。
三种角色¶
每条规则扮演三种角色之一,每种角色都可以用三种方式编写:
| 角色 | SQL | 外部调用 | 代码 |
|---|---|---|---|
| 数据集 | 视图、语句命令 | 返回行的 API 或存储过程 | 代码解析器 @table, @stream; python 或 script 命令 |
| 值 | 视图表达式、指标 | — | python 或 script 命令 |
| 变更 | INSERT, UPDATE, DELETE, 语句命令 |
API 写入、mutation、存储过程 | @command; python 或 script 命令 |
- 外部调用。 另一个系统的 GraphQL、REST 或 gRPC API,或数据库的存储过程,注册为命令。
- 以表作为参数。 命令可以接收表作为参数,因此一个数据集可以由其他数据集构建。
- 指标。 一个只定义一次的具名聚合,例如把
net_revenue定义为SUM(orders.amount) - SUM(orders.refunds)。每个查询按自己请求的维度展开它,因此数据仓库、仪表板和 API 读到的是同一个数字。
隐藏的副作用¶
隐藏的副作用是从规则的定义中看不出来的副作用。
每条规则都有名称和类型,并且已注册、受审计、受保护。 命令还可以要求在运行前获得审批。
| 可能的副作用 | 限制方式 | |
|---|---|---|
| SQL | 无 | — |
| 外部调用 | 另一个系统自身的工作流、通知和级联操作 | 出站允许列表(HTTP、gRPC);以密钥保存的凭据(存储过程、源操作) |
| 代码解析器 | 对外部服务和网络的调用;结果会变化;成本 | 固定的代码、固定的主机指纹、沙箱、计量 |
| 主机代码 | 能读取服务器能读取的一切(内存、凭据、文件、网络);可能耗尽服务器资源或使其宕机 | commands.allow_host_code, 默认关闭 |
外部调用的类型来自该系统自身的模式,或你注册命令时声明的参数和返回列。
代码解析器的代码按包版本、Git 提交或归档哈希固定,因此随模型一起推广。
主机代码是 python 或 script 命令。它在服务器内部运行,拥有服务器的全部访问权限,因此恶意、有缺陷或贪婪的代码能读取服务器能读取的一切,耗尽其资源或使其宕机。它很方便,在你信任这段代码时也是安全的。模型只保存一个引用(module:attr 或一条命令行),因此代码不会随推广一起迁移。它只在 commands.allow_host_code 开启的地方运行。
这些限制能约束副作用,但无法消除它们。外部系统或你自己的代码做什么,最终取决于它自己。
模型带给你什么¶
- 逻辑存在于模型中,而不是渠道中。每个渠道(SQL、GraphQL、REST、Arrow Flight、MCP 等)都读取相同的规则,因此同一个问题在任何地方都得到相同的答案。
- 每条规则都注册在一个域中,并有授权。
- 每条规则都以同样的方式治理,无论它如何编写。
- 每条规则都在血缘中可见。