基于规则架构风格对业务的重构
基于规则架构风格对业务的重构引言基于规则的架构风格常见的规则引擎JavaPythonZEN-Engine最后引言在现代系统开发中由于业务规则复杂且常常发生变化。例如电商平台针对某次活动实施打折促销需要根据用户是否为VIP是否被加入黑名单购物车的订单金额商品类型等因素最后计算出用户的打折率。这部分业务逻辑如果硬编码无论是直接if-else或者采用策略设计模式后期打折方式更改都将对源码进行维护。这不符合开闭原则。长远而言代码将难以维护。基于规则的架构风格基于规则的架构风格是解决以上问题的思路之一。将业务规则从业务代码中抽离为独立的规则文件面对业务规则的改变只需要修改或者增加业务规则文件不用重启系统规则自动更新。常见的规则引擎JavaJava技术栈里常见的规则引擎有DroolsEasy Rules。Drools将业务规则抽象为.drl文件。是Java里常用的规则引擎。Drools虽为Java生态主流但通过JPype可在Python中调用适合已有Drools资产的项目集成Easy Rules 极简API设计通过Java注解/YAML解耦业务逻辑与SpringBoot集成良好。PythonPython技术栈中常见的规则引擎有rule-enginePykePRISM-Python zen-engine。rule-engine支持类Python语法规则定义内置正则匹配、日期时间、类型校验等操作符适合数据过滤和简单业务规则。Pyke基于Prolog的逻辑编程引擎支持前向推理生成新事实和后向推理目标驱动规划需定义事实Facts和规则Rules。适用于专家系统、合规性检查等需要复杂逻辑链的场景。PRISM-Python从数据集中归纳可解释规则如“若feat_Ahot且feat_Cround则目标blue”支持分箱处理数值特征输出类似决策树但更简洁。ZEN Engine 是一款跨平台的开源业务规则引擎BRE。它使用Rust编写并为NodeJS**、**Python和Go提供原生绑定。ZEN 引擎允许从 JSON 文件加载并执行JSON 决策模型JDM。获取JSON的方式可以是文件系统数据库或者服务调用。ZEN Engine是一款高性能/分布式规则引擎设计为Drools的现代替代品。接下来将介绍ZEN Engine引擎集成在Python中的实践。ZEN-Engine首先安装zen-enginepip install zen-engine asynciodemo代码importasyncioimportzen业务规则 业务需求根据用户属性VIP等级、购物车金额和商品类别动态计算折扣规则如下 基础折扣购物车金额满 1000 元打 9 折满 2000 元打 85 折 VIP叠加折扣VIP 用户额外享受 5% 折扣可与基础折扣叠加 黑名单限制黑名单用户不享受任何折扣 商品类别排除数码类商品不参与折扣活动。 defloader(key):withopen(./jdm_directory/key,r)asf:returnf.read()asyncdefmain():enginezen.ZenEngine({loader:loader})# 测试用例1: VIP用户非数码商品高金额input1{user:{isVIP:True,isBlacklisted:False},cart:{total:2500,items:[{name:服装,category:clothing}]}}# 输出: finalRate 0.85 * 0.95 0.8075response1awaitengine.async_evaluate(discount.json,input1)print(response1)# 测试用例2: 含数码类商品input2{user:{isVIP:True,isBlacklisted:False},cart:{total:2500,items:[{name:手机,category:digital}]}}# 输出: eligibleAmount0 → finalRate1.0response2awaitengine.async_evaluate(discount.json,input2)print(response2)# 测试用例3: 黑名单用户input3{user:{isVIP:True,isBlacklisted:True},cart:{total:2500,items:[{name:服装,category:clothing}]}}# 输出: eligibleForDiscountFalse → finalRate1.0response3awaitengine.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作为规则文件易于理解和修改。是较好的一种方案。它可以无缝和Pythongo和nodejs集成。Java需要通过JNI进行集成。愿你我都能在各自的领域里不断成长勇敢追求梦想同时也保持对世界的好奇与善意!