跳转到主要内容
Chinese, Simplified

关键点

  • 外部规则简化了非开发人员主题专家的自定义业务逻辑。
  • JavaLambda语法比大型规则引擎更易于读写。
  • 通过将Java lambda语法与外部规则引擎相结合,主题专家可以使用低代码工具定制他们的应用程序。

复杂的企业应用程序通常具有不同的业务逻辑。这些条件和随后的系统操作(称为规则)是不断变化的,需要领域特定知识的参与,而不是技术和编程。因此,这些规则必须位于代码库之外,并且由具有核心领域专业知识的人编写,而这些人几乎没有技术/编程知识。一种特定类型的软件工具,称为规则引擎,满足这种环境。领域专家不一定是编码和技术的积极贡献者,就像品牌和营销团队一样,他们不需要了解组织门户和移动应用程序的底层技术,但很乐意轻松创作图像、横幅和其他内容,轻松使用Instagram手柄。Adobe aem是提供无代码/低代码内容创作的内容管理系统之一。新兴技术和云平台不断推出低代码和无代码的解决方案,这些解决方案已被广泛接受。本文介绍了一种轻量级方法,将业务操作外部化到低代码工具中,使具有相应领域专业知识的人员能够做出贡献。

虽然我们有很多这样的规则引擎,比如Drools(这是一个功能丰富的业务规则管理系统)、easy rules、Rule book、oracle rules SDK、Blaze(fico),IBMDecisionManager等。通过其各自的丰富功能(包括版本控制)以声明方式启用规则管理,对于许多应用程序来说通常非常有用。然而,在某些不太复杂的解决方案中,它们往往被证明是过度的,并且仍然没有得到充分利用。维护一个额外的组件更像是一项负债而不是一项资产。

在本文中,我们试图说明如何利用Java的固有特性以尽可能简单的方式将规则外部化,而不缩小到其他框架的任何可传递依赖性。当技术规则(用java编写的代码片段)需要外部化并且可能频繁更改时,这种方法非常有用。因此,无论框架如何,这种方法在任何Java生态系统中都具有同等的价值。为从外部源(例如,文件或URL)加载的规则(表示谓词或等效lambda表达式的java代码段)提供一个简单的基于POJO的声明性模型(其内容在java lambda样式表达式或java代码段中的外部源)它允许在应用程序外部编写规则,甚至不需要应用程序停机。我们可以轻松地将其与Spring微服务和云配置集成,无论是否使用云总线。该方法提供静态加密,以确保业务规则的安全性(机密性和完整性)。此外,除了支持Jasypt和Spring配置加密外,还可以插入任何自定义安全性。

规则引擎:常规方法

应对业务逻辑频繁变化的最传统、最理想的方法是规则引擎。规则通常是一组IF-THEN条件。

规则≡ 条件+动作

规则引擎是一种软件工具,简而言之,它为我们提供了在源代码之外设置规则的能力。它们允许半技术/非编程人员以各种方式设置规则。它们利用特定领域的知识,并提供GUI驱动的、直观的规则编写。Drools、DTRules、Oracle策略自动化系统、Easy Rules是规则引擎的几个示例。

传统的规则引擎使领域专家能够在代码库之外创建规则集和行为,对于复杂的大型业务环境非常有用。但对于较小且不太复杂的系统,考虑到其运行的内部部署或云基础设施的经常性成本、许可证成本等,它们往往被证明是过度使用的,并且仍然没有得到充分利用。对于小型团队来说,添加任何需要额外技能集的组件都是对其带宽的浪费。一些商业规则引擎具有陡峭的学习曲线。在本文中,我们试图说明如何成功地在源代码之外维护规则,以执行在Java技术堆栈(如Spring Boot)上运行的中型系统,从而使其他用户更容易自定义这些规则。。

这种方法适用于不能负担专门规则引擎、基础设施、维护、经常性成本等的团队,其领域专家有软件基础或团队内的人戴多个帽子。

这种极简主义的方法适合创业者——正如我们一直看到的那样,许多大公司都是以小团队、低预算、优秀人才和创新理念创立的。虽然团队的规模可能不是完全确定的,但如果团队规模为10人或更少,并且存在这些限制,则可以充分利用这种方法。

规模更大的团队可能也会受益,因为他们减少了专用规则引擎的使用,除非需要商用规则引擎的所有酷和主要功能。

结果的概述和新颖性

图1示出了机构的更高层次的功能概述,而下面列出的步骤描述了这些组件中的每一个:

图1

  • 配置存储规则,这些规则只不过是简单的java方法或布尔表达式。为了减少Java语法的冗长,我们还提供了Lamda和方法引用,从而在Java方法的语法上提供了额外的抽象层。
  • 配置存储由领域专家编写的规则。这些都是有效的陈述,写得像一个lambda机构,捕捉专家的意图。例如,要将ESRB成熟的视频游戏作为适龄产品,领域专家可以写:

                     customer->customer.getAge()>=17

  • 配置可以是一个文件或原始字节流,也可以是一个URL,并且可以放在任何地方-提供大量的选项,例如——
    • 本地磁盘上的文件
    • 数据库–SQL和NoSQL
    • 远程网络位置,如HTTP URL、原始TCP套接字等。
    • 云位置,如AWS S3存储桶或Google驱动器位置等。
  • 该框架提供了对源代码、编码、安全性和传输的总体抽象。任何标准或专有编解码器都可以用来存储和检索规则,这同样适用于两种主要的安全模式——机密性和完整性。在这方面,可以编写自己的适配器。仅此而已,其检索过程的最后一次飞跃应该产生Java表达式或谓词中的规则。
  • 通过一些额外的努力,该机制可能会通过任何标准协议(如WebSocket、HTTP/2、XMPP、MQTT)将流规则适合浏览器。这种方法甚至可以与Web组件结合在一起,在Web浏览器中集成安全、复杂的规则。

入门:

虽然在开始使用SDK之前最好先了解一下它的结构,但为了方便和简单,我们颠倒了顺序。在本节中,我们用简单的问题陈述或用例来说明规则是如何外部化的。

先决条件

消费者必须事先接触过Java SE 1.8或更高版本的软件开发。除此之外,还需要一台带有JavaSE1.8和IDE的标准机器。在本文中,Eclipse将被用作IDE,MAVEN将被用作所有插图中的构建工具。

步骤1:

从这里下载库并将其保存在本地路径上,例如D:\externalize-rules.jar。这是完整的源代码。

步骤2:

创建一个Maven项目,编译器源代码级别设置为1.8,依赖项如右图所示。

问题陈述:一家有价值的服装零售店在不同的、频繁的场合向其客户提供折扣,并有一套不同的食用标准,如职业、收入水平、位置、教育水平等。为了简单起见,我们假设一个简单的客户模型类-

我们进一步假设一个类别,Repo_Customer,它作为客户信息的来源,并根据特定的资格标准挑选有资格享受折扣的客户。

这么久了,一切都很平常;我们可以使用以下任意一种或组合来定义折扣资格规则:

步骤3:

值得注意的是,lambda没有那么冗长,主题专家可能会使用这些定义规则,而对Java编程语言没有深入的了解。

我们将折扣规则保留在代码库之外,因为它可能会频繁更改。并在所有可用选项中选择最简单的选项–我们将规则存储在本地磁盘上的文件中。SDK帮助加载并执行这些规则。以下文件存储在本地磁盘上:

 

我们在文件中创建了自己的标记标准(可以是任何人设计和实现的,而不限于任何特定实现):

<RuleName><ColDelimiter><TargetModel><ColDelimiter><LamdaeExpression>

<RowDelimiter>

步骤4:

最后,我们编写这个小代码来检查规则,看看规则是否执行:

SDK内部

现在,我们已经成功地启动并编写了一个简单的快速启动程序,我们可以返回并开始探索这个SDK所做的事情,并附有插图。

总体包结构

com.yourcompany.libs.externalizedrules.poc.RuleExecutor<T,R>

这是一个定义规则执行方法的抽象类。这需要由为这两个抽象方法提供实现的消费者应用程序代码进行子类化。R表示决策的结果,T表示决策所基于的输入对象。例如,如果我们想定义一个过滤掉所有60岁以上客户的规则,我们需要将.RuleExecutor<DTO_Customer,Boolean>

com.yourcompany.libs.externalizedrules.poc.models.RuleExecutionConfig

此类从配置源加载规则,并且必须事先初始化。本课程负责以下事项-

  • 从配置源加载原始规则库
  • 从基于规则的代码片段(例如Lamda)合成java代码
  • 编译综合java代码
  • 最后,将类加载到主内存中,以便执行规则

已定义框架的具体实现

我们已经在[2]中使用了谓词执行器。这是com.yourcompany.libs.externalizedrules.poc.

RuleExecutor<T,R>的一个特殊实现,其计算结果为布尔值。

它执行以下3项任务:

  • 它加载计算结果为真或假的规则。这意味着,它将原始规则库或Lamda表达式转换为一个java方法,该方法接受一个对象并返回一个布尔值,即形成一个java.util.function.Predicate<java.lang.Object>。
  • 该框架,特别是PredicateExecutionContextLoader,它是RuleExecutionContextLoader的子类,为这些底层任务提供了抽象。
  • 根据一些标准/定制的编解码器,如果需要加载加密和/或编码/模糊的字节序列,则必须重写方法com.yourcompany.libs.externalizedrules.poc.services.PredicateExecutionContextLoader.load(InputStream)。
  • 这个方法的一个优点是,它在字节处工作。因此,可以从文件系统、URL、云存储、数据库等加载规则。
  • 它从本地文件加载原始规则。此方法getConfigStream()返回java.io.inputStream。它提供了从任何地方加载原始字节的灵活性。

我们将很快说明如何从几个主要和常见的源加载规则。

一些公共源代码的实现

在本节中,我们将探讨几个常见但重要的资源,从中可以加载配置。

从HTTP上的远程URL

按正确的顺序排列

我们已经粗略地将SDK的实用性视为PoC。然而,作者们正致力于把它塑造成一个开源项目。本节是本文的最后一节,我们旨在强调其一些显著特征:

  • 该框架可以轻松地与不同的安全框架(如JASYPT)一起开箱即用,以确保业务规则的完整性和机密性,因此既不能滥用明文业务规则,也不能插入自己的规则。
  • 因为它是纯java的,所以它完全适合所有流行的框架,例如
    • Spring and Spring BOOT
    • JSF
    • Play
    • ZK
  • 它非常适合在没有云总线和有云总线的情况下进行Spring云配置。
  • 协议中立性使其可用于任何云提供商或本地存储上的任何源
  • 也可以在任何云提供商或本地存储上实现自己的自定义编解码器。
  • 我们可以使用JMX钩子重新加载配置,而无需重新启动应用程序。

 

原文:https://www.infoq.com/articles/java-external-rules-engine/

本文:http://jiagoushi.pro/node/1531

Article
知识星球
 
微信公众号
 
视频号