规则分类与建模:从混乱到有序
数据校验规则,说白了就是一堆「如果…就报错」的逻辑。但项目一大了,规则成百上千条,如果没有分类和建模,那就是一团乱麻。我见过不少团队,规则写在各种文档里、代码注释里、甚至钉钉聊天记录里——嗯,这种项目我一般不敢碰。
这一章,我们就来聊聊怎么给规则分门别类,以及怎么设计一个能支撑持续集成的规则元数据模型。
规则分类:四种核心类型
我个人习惯把校验规则分成四类。这四类不是拍脑袋想的,是我在几个大型数据平台项目里反复踩坑后总结出来的。
| 分类 | 核心关注点 | 典型场景 | 校验时机 |
|---|---|---|---|
| 格式规则 | 数据的外观是否符合规范 | 手机号11位、邮箱含@、日期格式YYYY-MM-DD | 数据入库前 |
| 逻辑规则 | 数据内部是否自洽 | 开始日期 ≤ 结束日期、年龄≥0 | 数据入库后 |
| 业务规则 | 数据是否符合业务语义 | 订单金额=单价×数量、VIP用户等级≥3 | 业务处理中 |
| 引用规则 | 跨表/跨系统的数据一致性 | 用户ID在用户表中存在、订单号不重复 | 数据关联时 |
一个真实教训: 我曾经接手过一个项目,团队把所有规则都塞进「业务规则」一个分类里。结果呢?格式校验和业务逻辑混在一起,改一个手机号格式规则,居然要拉上业务方一起评审。分类不清,维护成本直接翻倍。
格式规则:最基础也最容易忽略
格式规则是最简单的,但往往也是出问题最多的。你想想看,一个日期字段,有的系统传「2024-01-01」,有的传「2024/01/01」,还有的传「2024年1月1日」。如果不做格式校验,下游系统直接崩溃。
我建议格式规则要做到「三明确」:
- 明确字符集: 只允许数字?还是允许字母+数字?
- 明确长度范围: 固定长度还是可变长度?最大最小是多少?
- 明确模式模板: 用正则表达式还是枚举值?
小技巧: 格式规则尽量用正则表达式表达,不要写死代码。这样规则变更时,只需要改配置,不用改代码。我在一个金融项目里,把200多条格式规则全部抽成正则配置,上线后规则变更效率提升了80%。
逻辑规则:数据自洽的守护者
逻辑规则关注的是数据内部的「合理性」。比如一个订单的「发货时间」不能早于「下单时间」,一个用户的「注册日期」不能晚于「当前日期」。这些规则看起来简单,但一旦漏掉,后果很严重。
我记得有一次,数据仓库里跑出来一个报表,显示某用户「注册于2025年,但消费记录在2023年」。排查了半天,发现是逻辑校验没做好,ETL脚本里把时间字段搞混了。从那以后,我对逻辑规则格外重视。
逻辑规则通常涉及:
- 字段间比较: A字段 > B字段?A字段 = B字段 + C字段?
- 范围约束: 数值在[min, max]之间?日期在某个时间段内?
- 枚举约束: 状态值必须在预定义的集合中?
业务规则:最复杂也最灵活
业务规则是四类规则里最「重」的。它直接反映业务逻辑,比如「VIP用户享受8折优惠」「订单金额超过1000元需要审批」。这类规则的特点是:变化频繁、依赖上下文、往往需要关联多个字段甚至多个系统。
我建议业务规则单独管理,不要和格式、逻辑规则混在一起。为什么呢?因为业务规则的变化频率远高于其他规则。你想想看,促销活动每周都在变,对应的业务规则也得跟着变。如果混在一起,每次改业务规则都要重新部署整个校验服务,太折腾了。
避坑指南: 我曾经在一个项目中,把业务规则写死在Java代码里。结果业务方每周提3次需求改规则,每次都要走完整的CI/CD流程。后来我改用规则引擎(Drools)把业务规则抽成配置文件,变更效率提升了10倍。记住:业务规则一定要可配置化。
引用规则:跨系统的信任桥梁
引用规则解决的是「数据在A系统存在,在B系统是否也存在」的问题。比如:订单表中的用户ID,必须在用户表中存在;交易记录中的产品编码,必须在产品目录中存在。
这类规则最头疼的地方在于:它依赖其他系统的数据。如果被引用的系统数据还没准备好,引用校验就会失败。所以引用规则通常需要设置「延迟校验」机制——等被引用数据到位后再校验。
引用规则的常见模式:
- 存在性校验: 外键值在目标表中存在
- 唯一性校验: 某个字段在目标表中不重复
- 一致性校验: 两个系统中的同一字段值保持一致
规则元数据模型设计
分类搞清楚了,接下来就是建模。规则元数据模型,说白了就是「用什么样的数据结构来描述一条规则」。我设计过好几版模型,最终沉淀下来一个比较通用的版本。
-- 规则元数据核心表结构(简化版)
CREATE TABLE rule_metadata (
rule_id VARCHAR(64) PRIMARY KEY, -- 规则唯一ID
rule_name VARCHAR(128) NOT NULL, -- 规则名称
rule_type ENUM('FORMAT','LOGIC','BUSINESS','REFERENCE'), -- 规则分类
rule_status ENUM('ACTIVE','INACTIVE','DEPRECATED'), -- 规则状态
rule_priority INT DEFAULT 5, -- 优先级 1-10
rule_expression TEXT NOT NULL, -- 规则表达式(如正则/SQL/脚本)
rule_description TEXT, -- 规则描述
data_domain VARCHAR(64), -- 所属数据域
table_name VARCHAR(128), -- 目标表
column_name VARCHAR(128), -- 目标字段
error_code VARCHAR(32), -- 错误码
error_message VARCHAR(512), -- 错误提示
created_by VARCHAR(64), -- 创建人
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
关键设计要点:
- rule_expression: 这是核心字段,存储规则的具体逻辑。格式规则存正则,逻辑规则存SQL条件,业务规则存脚本或规则引擎DSL。
- rule_status: 支持规则的生命周期管理。新规则先设为INACTIVE,测试通过后再激活。
- error_code + error_message: 标准化错误输出,方便下游系统统一处理。
规则分类与元数据的关系
为了让你更直观地理解,我画了一张图。这张图展示了四种规则分类如何映射到元数据模型的不同字段上。
规则元数据的扩展属性
除了核心字段,实际项目中还需要一些扩展属性。我列几个常用的:
| 扩展属性 | 类型 | 说明 |
|---|---|---|
| rule_version | INT | 规则版本号,每次变更递增 |
| effective_date | DATE | 规则生效日期 |
| expiry_date | DATE | 规则失效日期(用于临时规则) |
| severity | ENUM | 严重级别:CRITICAL / WARNING / INFO |
| owner_team | VARCHAR | 规则负责人团队 |
| source_system | VARCHAR | 规则来源系统 |
我的经验: 扩展属性不要一开始就设计得太全。我见过有人设计了50多个字段的元数据表,结果80%的字段都是空的。建议先设计核心字段,随着项目推进再逐步添加扩展属性。敏捷一点,别想着一步到位。
规则建模的常见误区
最后,分享几个我在项目中踩过的坑:
- 误区一:规则和校验逻辑耦合太深。 规则元数据应该只描述「校验什么」,不描述「怎么校验」。校验引擎是另一层的事。
- 误区二:忽略规则版本管理。 规则是会变的。没有版本管理,出了问题你都不知道是哪个版本的规则在生效。
- 误区三:规则分类太细或太粗。 分类太细,管理成本高;分类太粗,规则混在一起难以维护。四种分类刚刚好。