基于规则架构风格对业务的重构
基于规则架构风格对业务的重构
- 引言
- 基于规则的架构风格
- 常见的规则引擎
- Java
- Python
- ZEN-Engine
- 最后
引言
在现代系统开发中,由于业务规则复杂且常常发生变化。例如电商平台针对某次活动实施打折促销,需要根据用户是否为VIP,是否被加入黑名单,购物车的订单金额,商品类型等因素最后计算出用户的打折率。这部分业务逻辑如果硬编码,无论是直接if-else或者采用策略设计模式,后期打折方式更改,都将对源码进行维护。这不符合开闭原则。长远而言,代码将难以维护。
基于规则的架构风格
基于规则的架构风格是解决以上问题的思路之一。将业务规则从业务代码中抽离为独立的规则文件,面对业务规则的改变,只需要修改或者增加业务规则文件,不用重启系统,规则自动更新。
常见的规则引擎
Java
Java技术栈里常见的规则引擎有Drools,Easy Rules。
-
Drools将业务规则抽象为.drl文件。是Java里常用的规则引擎。Drools虽为Java生态主流,但通过JPype可在Python中调用,适合已有Drools资产的项目集成
-
Easy Rules 极简API设计,通过Java注解/YAML解耦业务逻辑,与SpringBoot集成良好。
Python
Python技术栈中常见的规则引擎有rule-engine,Pyke,PRISM-Python, zen-engine。
- rule-engine支持类Python语法规则定义,内置正则匹配、日期时间、类型校验等操作符,适合数据过滤和简单业务规则。
- Pyke基于Prolog的逻辑编程引擎,支持前向推理(生成新事实)和后向推理(目标驱动规划),需定义事实(Facts)和规则(Rules)。适用于专家系统、合规性检查等需要复杂逻辑链的场景。
- PRISM-Python从数据集中归纳可解释规则(如“若feat_A=hot且feat_C=round则目标=blue”),支持分箱处理数值特征,输出类似决策树但更简洁。
- ZEN Engine 是一款跨平台的开源业务规则引擎(BRE)。它使用Rust编写,并为NodeJS**、**Python和Go提供原生绑定。ZEN 引擎允许从 JSON 文件加载并执行JSON 决策模型(JDM)。获取JSON的方式可以是文件系统,数据库或者服务调用。ZEN Engine是一款高性能/分布式规则引擎,设计为Drools的现代替代品。接下来将介绍ZEN Engine引擎集成在Python中的实践。
ZEN-Engine
首先安装zen-engine
pip install zen-engine asyncio
demo代码
import asyncio
import zen"""业务规则:
业务需求:根据用户属性(VIP等级、购物车金额)和商品类别动态计算折扣,规则如下:
基础折扣:购物车金额满 1000 元打 9 折,满 2000 元打 85 折;
VIP叠加折扣:VIP 用户额外享受 5% 折扣(可与基础折扣叠加);
黑名单限制:黑名单用户不享受任何折扣;
商品类别排除:数码类商品不参与折扣活动。"""def loader(key):with open("./jdm_directory/" + key, "r") as f:return f.read()async def main():engine = zen.ZenEngine({"loader": loader})# 测试用例1: VIP用户+非数码商品+高金额input1 = {"user": {"isVIP": True, "isBlacklisted": False},"cart": {"total": 2500,"items": [{"name": "服装", "category": "clothing"}]}}# 输出: finalRate = 0.85 * 0.95 = 0.8075response1 = await engine.async_evaluate("discount.json", input1)print(response1)# 测试用例2: 含数码类商品input2 = {"user": {"isVIP": True, "isBlacklisted": False},"cart": {"total": 2500,"items": [{"name": "手机", "category": "digital"}]}}# 输出: eligibleAmount=0 → finalRate=1.0response2 = await engine.async_evaluate("discount.json", input2)print(response2)# 测试用例3: 黑名单用户input3 = {"user": {"isVIP": True, "isBlacklisted": True},"cart": {"total": 2500,"items": [{"name": "服装", "category": "clothing"}]}}# 输出: eligibleForDiscount=False → finalRate=1.0response3 = await engine.async_evaluate("discount.json", input3)print(response3)asyncio.run(main())
在该demo中,规则文件在项目根目录的jdm_directory/discount.json中。JDM通过在线编辑器GoRules Editor进行编辑。然后下载Json文件即可。
下载下来的json文件为:
{"contentType": "application/vnd.gorules.decision","nodes": [{"type": "inputNode","content": {"schema": "{\n \"type\": \"object\",\n \"properties\": {\n \"user\": {\n \"type\": \"object\",\n \"properties\": {\n \"isVIP\": {\n \"type\": \"boolean\"\n },\n \"isBlacklisted\": {\n \"type\": \"boolean\"\n }\n }\n },\n \"cart\": {\n \"type\": \"object\",\n \"properties\": {\n \"total\": {\n \"type\": \"integer\"\n },\n \"items\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"name\": {\n \"type\": \"string\"\n },\n \"category\": {\n \"type\": \"string\"\n }\n }\n }\n }\n }\n }\n }\n}"},"id": "f78943f3-ae56-44cf-b553-b4c33930c90a","name": "Request","position": {"x": 160,"y": 245}},{"type": "decisionTableNode","content": {"hitPolicy": "first","rules": [{"_id": "84ac149e-0ed9-468a-874b-8cdf4ade5431","_description": "","fa385065-e404-4145-af2a-8e65c9fec745": "true","b62dfc17-126b-42cd-b311-604e1abf9d27": "false"},{"_id": "9e1144d3-e34f-481b-ac29-04b0a00292e5","_description": "","fa385065-e404-4145-af2a-8e65c9fec745": "false","b62dfc17-126b-42cd-b311-604e1abf9d27": "true"}],"inputs": [{"id": "fa385065-e404-4145-af2a-8e65c9fec745","name": "UserIsBlacked","field": "user.isBlacklisted"}],"outputs": [{"id": "b62dfc17-126b-42cd-b311-604e1abf9d27","name": "EligibleForDiscount","field": "eligibleForDiscount"}],"passThrough": false,"inputField": null,"outputPath": null,"executionMode": "single","passThorough": false},"id": "cd36e9a3-e711-4b4f-b310-a643a9495a76","name": "Black List","position": {"x": 505,"y": 245}},{"type": "decisionTableNode","content": {"hitPolicy": "first","rules": [{"_id": "a4f6a175-ebc1-41c0-8e10-014b9850ca7c","3232fa61-73dc-4a12-802a-51ee0b01e8a0": "some(cart.items, #.category == \"digital\")","8dc679d2-52f2-4cd3-9d8c-e03ab9000111": "false"},{"_id": "f358c02e-1261-498f-984b-44e04949fcf6","3232fa61-73dc-4a12-802a-51ee0b01e8a0": "not(some(cart.items, #.category == \"digital\"))","8dc679d2-52f2-4cd3-9d8c-e03ab9000111": "true"}],"inputs": [{"id": "3232fa61-73dc-4a12-802a-51ee0b01e8a0","name": "Category","field": "cart.items"}],"outputs": [{"id": "8dc679d2-52f2-4cd3-9d8c-e03ab9000111","name": "EligibleAmount","field": "eligibleAmount"}],"passThrough": false,"inputField": null,"outputPath": null,"executionMode": "single","passThorough": false},"id": "16efb624-32f0-4042-b283-329408b28e69","name": "Category Filter","position": {"x": 510,"y": 375}},{"type": "decisionTableNode","content": {"hitPolicy": "first","rules": [{"_id": "ce872485-0bc5-442b-bae8-d9fdc632d7ed","03bd13c6-9717-42d9-9251-a89df9f59d20": "< 1000","6cbf8303-7c4a-4ef2-9f34-0a6fb4931e0c": "1"},{"_id": "502c794c-844d-41d9-b0af-57d3dacc56e3","03bd13c6-9717-42d9-9251-a89df9f59d20": ">= 1000 and < 2000","6cbf8303-7c4a-4ef2-9f34-0a6fb4931e0c": "0.9"},{"_id": "abef895e-b0a7-4a6f-9e7b-3518195307d8","03bd13c6-9717-42d9-9251-a89df9f59d20": ">= 2000","6cbf8303-7c4a-4ef2-9f34-0a6fb4931e0c": "0.85"}],"inputs": [{"id": "03bd13c6-9717-42d9-9251-a89df9f59d20","name": "CartTotal","field": "cart.total"}],"outputs": [{"id": "6cbf8303-7c4a-4ef2-9f34-0a6fb4931e0c","name": "BaseRate","field": "baseRate"}],"passThrough": true,"inputField": null,"outputPath": null,"executionMode": "single","passThorough": false},"id": "813891ff-8959-40cb-aeca-232024d6386c","name": "Base Rate","position": {"x": 505,"y": 505}},{"type": "decisionTableNode","content": {"hitPolicy": "first","rules": [{"_id": "bcfe88fe-00d2-4854-95fc-bbce98859684","f7e3998b-4f19-4080-b660-d1abc6f0d457": "true","8f8e0fc3-5570-46f1-b108-5868194b149e": "","727d23e0-8bf2-499a-940a-ac3bad8e3e67": "baseRate * 0.95"},{"_id": "e8538446-cdc2-4fed-8a0b-ad091121d44a","f7e3998b-4f19-4080-b660-d1abc6f0d457": "false","8f8e0fc3-5570-46f1-b108-5868194b149e": "","727d23e0-8bf2-499a-940a-ac3bad8e3e67": "baseRate"}],"inputs": [{"id": "f7e3998b-4f19-4080-b660-d1abc6f0d457","name": "VIP","field": "user.isVIP"},{"id": "8f8e0fc3-5570-46f1-b108-5868194b149e","name": "BaseRate","field": "baseRate"}],"outputs": [{"id": "727d23e0-8bf2-499a-940a-ac3bad8e3e67","name": "VIPRate","field": "vipRate"}],"passThrough": false,"inputField": null,"outputPath": null,"executionMode": "single","passThorough": false},"id": "3a76eb8e-41f5-4c04-94ab-3c3460657222","name": "VIPRate","position": {"x": 850,"y": 510}},{"type": "expressionNode","content": {"expressions": [{"id": "7c44b4db-5f1e-425c-9ce2-1b3fa89c493e","key": "finalRate","value": "not(eligibleForDiscount) ? 1 : (not(eligibleAmount) ? 1.0 : vipRate ) "}],"passThrough": false,"inputField": null,"outputPath": null,"executionMode": "single"},"id": "6bc22483-a157-4561-adfb-cbff2abc35c9","name": "Exception","position": {"x": 1190,"y": 355}},{"type": "outputNode","content": {"schema": ""},"id": "b6c9f783-1713-4cec-b743-7e15272918ff","name": "Response","position": {"x": 1605,"y": 355}}],"edges": [{"id": "a98a6bd1-f9a2-44ba-86d8-6e08698876cf","sourceId": "f78943f3-ae56-44cf-b553-b4c33930c90a","type": "edge","targetId": "cd36e9a3-e711-4b4f-b310-a643a9495a76"},{"id": "0d62f9d3-7686-4238-a54a-c409f20b85fe","sourceId": "f78943f3-ae56-44cf-b553-b4c33930c90a","type": "edge","targetId": "16efb624-32f0-4042-b283-329408b28e69"},{"id": "5912f416-e323-4083-b6d5-3d5d3133e2cd","sourceId": "f78943f3-ae56-44cf-b553-b4c33930c90a","type": "edge","targetId": "813891ff-8959-40cb-aeca-232024d6386c"},{"id": "c23550c9-fc1a-40cf-afc4-446f8e033292","sourceId": "813891ff-8959-40cb-aeca-232024d6386c","type": "edge","targetId": "3a76eb8e-41f5-4c04-94ab-3c3460657222"},{"id": "4172df7f-d961-4b21-a8a8-9f5b42f4c817","sourceId": "cd36e9a3-e711-4b4f-b310-a643a9495a76","type": "edge","targetId": "6bc22483-a157-4561-adfb-cbff2abc35c9"},{"id": "ca44d896-03d4-4bf9-abe6-0eed9ff58bc3","sourceId": "16efb624-32f0-4042-b283-329408b28e69","type": "edge","targetId": "6bc22483-a157-4561-adfb-cbff2abc35c9"},{"id": "74d2e622-5d43-485d-b9f8-9d7e611747bb","sourceId": "3a76eb8e-41f5-4c04-94ab-3c3460657222","type": "edge","targetId": "6bc22483-a157-4561-adfb-cbff2abc35c9"},{"id": "b7263fc5-a2b0-4322-bb4d-53073fe2e0ac","sourceId": "6bc22483-a157-4561-adfb-cbff2abc35c9","type": "edge","targetId": "b6c9f783-1713-4cec-b743-7e15272918ff"}]
}
具体使用可以参考官方手册Decision Management | GoRules Rules Engine
最后
zen-engine具有高性能,功能强大,采用json作为规则文件,易于理解和修改。是较好的一种方案。它可以无缝和Python,go和nodejs集成。Java需要通过JNI进行集成。
愿你我都能在各自的领域里不断成长,勇敢追求梦想,同时也保持对世界的好奇与善意!