基于规则架构风格对业务的重构

发布于:2025-07-25 ⋅ 阅读:(17) ⋅ 点赞:(0)

基于规则架构风格对业务的重构

    • 引言
    • 基于规则的架构风格
    • 常见的规则引擎
      • 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.8075
    response1 = 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.0
    response2 = 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.0
    response3 = 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进行集成。


愿你我都能在各自的领域里不断成长,勇敢追求梦想,同时也保持对世界的好奇与善意!


网站公告

今日签到

点亮在社区的每一天
去签到