风控域(risk)五层架构落地
一篇关于分层的立场评述:五层架构的价值不在整齐,而在于让风险判定的每个部件都可替换——规则能运营而不用发版。
附件
- fig-risk-judge-flow.png(122 KB)下载
风控域(risk)五层架构落地
「不以规矩,不能成方圆。」规矩所以为规矩,因为它敢于在这一票说「不予批准」。本篇复盘 risk 域从零落地:风险规则、黑白名单、风险事件、风险评分一体化判定,五层齐备,集成测试 11 个全部通过。
一、界定:分层的意义是「让某物可替换」
六边形架构最常被读歪成一种目录仪式——api 放契约、domain 放实体、infrastructure 放 SQL,看起来工整,其实什么也没保证。分层的真正承诺只有一个:让某物可替换。判定引擎只依赖领域抽象,那么「规则怎么存、名单放哪里、事件怎么流」全都是可换的零件,而不是焊死在底座上的结构钢筋。
| 层 | 交付物 | 职责 |
|---|---|---|
| api | 风险判定门面 + command/result | 跨域 Dubbo 契约,POJO 实现 Serializable |
| domain | RiskRule / RiskEvent / RiskBlacklist / RiskWhitelist | 纯领域模型与仓储接口,零框架依赖 |
| application | 规则管理 / 风险事件查询 / 风险判定 | 用例编排与功能权限校验统一入口 |
| infrastructure | REST 控制器、MyBatis-Plus 仓储、Liquibase schema、判定引擎 | 技术实现与持久化 |
| bootstrap | 微服务启动编排 | 独立部署形态 |
二、为什么必须:风控的两条命门矛盾
风控域与生俱来两个别处少见的矛盾。其一,风险规则要能在运营态改变——凌晨三点把误杀账户加回白名单,不能靠发版解决;其二,名单与规则绝不能一起失效——名单错了要能摁住规则,规则错了要能被名单逃过,二者的错误从来不商量着一起发生。
第一个矛盾要求规则与名单成为可运营的数据而不只是常量;第二个矛盾要求它们在判定时序上存在短路关系。两个答案汇成同一个结构:用例编排归 application,判定自洽归 domain,可变的存储归 infrastructure——语义变化才发版,名单运营不发版。
三、机理:宪法先于法律
判定时序是对这套结构最贴切的注脚:
judge(subject, action):
1. 装载启用规则与名单
2. whitelist 命中 → allow(短路)
3. blacklist 命中 → deny(短路,写 RiskEvent)
4. 逐条评估 RiskRule → 累计风险评分
5. 评分越过阈值 → deny,否则 allow
6. 全过程写 RiskEvent黑白名单短路优先是全文最重要的地基:白名单命中直接放行,黑名单命中直接拒绝,任何一方都不进入规则评估——避免规则误杀。它背后的次序观是:名单是判定的宪法,规则是判定的法律;宪法一句顶法律万句,先把宪法说清楚,法律才有发言的资格。无论结论如何都写 RiskEvent,判定过程自身也可被事后审计。
api 契约与其他域保持同一消费模式(单体本地 bean / 微服务 Dubbo Reference),异常收敛到 domains/common/domain 的三层体系并由 GlobalExceptionHandler 统一序列化——新域不再发明规范,规范本来就在走廊尽头。
四、思辨:分层崇拜也是税
不能唱满赞歌。分层本身是一笔持续支付的成本:每一次「看得见却够不着」的绕行、每一份为接口而接口的样板,都在交税。若一个业务只有一条判定规则和一份名单,五层齐备就是给草棚镶上大理石。
六边形架构是付得起这笔税的复杂业务的门票。risk 域值得:规则、名单、事件、评分的复杂度名实相副。但下一个域来的时候,先审复杂度、再定层数——结构服务于业务,而不是反客为主。五层是答案,不是信仰。
五、回到现实:五步接入清单
- domains/pom.xml 与聚合 bootstrap/pom.xml 按依赖顺序接入(domain → application → infrastructure → bootstrap);
- 本域 Liquibase master 按域路径 include,应用配置 spring.liquibase.change-log 指向本域文件;
- 跨域端口双向打通:对外暴露 api 层 Dubbo 门面,对内消费 tag/auth 走 api 门面(单体本地 bean / 微服务 profile ReferenceBean);
- 鉴权收口:动作级资源编码 api.<domain>.<entity>.<action> seed + 应用层统一校验入口;数据权限单独经 data_permission_policies 表达;
- 日志对齐 audit 约定:控制器 LogUtils.interfaceLog、服务 DomainLogger、管理端写操作 @AuditLog。
后续演进(命中统计报表、名单批量导入、与 workflow 联动的人工复核)都是在这块地基上添砖,而不是另起炉灶。