作者 | Soham Sengupta、Srijeeb Roy
譯者 | 陳亮芬
策劃 | 丁曉昀
複雜的企業應用程式通常有着不同的業務邏輯。這些業務邏輯中的前置條件和後續系統動作(也就是我們所說的規則)總是變化的。而且,比起技術和程式設計,我們這裡所說的規則更需要特定領域的知識介入。我們在實作這些規則時不應老想着靠代碼,反而應該駐留在代碼庫之外,由具有核心領域專業知識的人去進行規則編寫(他們隻需要具備極少的技術及程式設計知識)。有一種特定類型的軟體工具,也就是規則引擎可以幫助解決難以确定的業務規則需求。領域專家們并不需要擅長編碼和技術,就像企業的品牌和營銷團隊不需要知道企業門戶和移動應用程式的底層技術,但他們需要善于撰寫編輯圖像、橫幅和其他内容等(這些工作用 Instagram 賬号就能輕松做到)。Adobe aem 是提供無代碼 / 低代碼内容創作的内容管理系統之一。新興技術和雲平台不斷提出低代碼和無代碼的解決方案,而且這些解決方案也獲得了需求市場廣泛的接受。本文介紹了一種将業務操作外部化到低代碼工具中實作的輕量級方法,使得具有各自領域專業知識的人員也可以實作業務規則方面幫上忙。
雖然我們已經可以在市場上選用到很多規則引擎,比如 Drools(它是一個功能豐富的業務規則管理系統)、Easy Rules、Rule Book、Oracle Rules SDK、Blaze (fico)、IBM Decision Manager 等等。這些規則引擎通過各自的豐富特性(包括版本控制)以聲明的方式啟用規則管理,這對許多應用程式來說通常非常有用。然而,在某些不太複雜的解決方案中,它們往往是多餘的,并沒有得到充分利用。反而顯得維護這個額外的元件更像是一種負債而不是一種資産,開銷大于實際使用價值。
在本文中,我們試圖說明如何利用 Java 的固有特性,用盡可能簡單的方式實作外部化規則,而不局限于附加架構的任何傳遞依賴。當技術規則(用 Java 編寫的代碼片段)需要外部化并且可能頻繁更改時,這種方法非常有用。是以,這種方法在任何 Java 生态系統中都具有同等的價值,無論架構是什麼。為從外部源(例如檔案或 URL)加載的規則提供一個簡單的基于聲明式模型的 POJO,這些規則是代表一個謂語或者一個等同于 lambda 表達式的 Java 代碼片段。外部源的内容是 Java lambda 風格的表達式或 Java 代碼片段,來源範圍包括本地資料庫及雲資源,這樣就可以實作在應用程式之外編寫規則,甚至不需要應用程式停機。我們可以很容易地将其與 Spring 微服務和雲配置進行內建,用不用雲總線均可。這種方法提供靜止加密以確定業務規則的安全性(機密性和完整性)。另外,除了支援 Jasypt 和 Spring Config Ciphering 之外,任何自定義安全性都可以插入其中。
規則引擎:傳統的方式
處理業務邏輯頻繁變化的最傳統和最理想的方法是規則引擎。規則通常是一組 IF-THEN 條件。
規則≡前置條件 + 後續行動(RULE≡ CONDITION+ACTION)
規則引擎是軟體工具,簡單來說,為我們提供了在源代碼之外設定規則的能力。規則引擎使得半技術人員 / 非程式設計人員以不同的方式設定規則。利用領域特定知識,這些規則引擎可以提供 GUI 驅動的、直覺的規則編寫。Drools,DTRules,Oracle Policy Automation system,Easy-Rules 等就是常用的規則引擎。
傳統的規則引擎幫助領域專家能夠脫離代碼庫編寫規則集和行為,這對于複雜的大型業務環境非常有用。但對于較小的并不複雜的系統來說,考慮運作在本地或雲基礎設施上的經常性成本以及許可證成本等,結果證明,往往對于這類小系統、簡單系統來說,規則引擎功能必要性大不,難以得到充分利用。對于小型團隊來說,添加任何需要額外技能集的元件都是對帶寬的浪費。一些商業規則引擎有陡峭的學習曲線,一直在追求更好的規則引擎性能,對使用者使用的成本效益考慮得比較少。在本文中,我們試圖說明如何成功地在源代碼之外維護規則,以執行在 Java Tech-Stack(像 Spring Boot)上運作的中型系統,使其他使用者自定義定制這些規則更容易。
這種方法适用于那些無法負擔專用的規則引擎開銷及其基礎設施、維護、經常性成本等等成本的團隊,并且團隊中的領域專家具有軟體基礎,或者團隊成員身兼數職。
這種極簡主義的方法适合創業的人。深究大公司的起源,我們不難發現,許多大公司一開始也是小團隊,預算都必須花在刀刃上,擁有優秀的人才和創新的想法。是以,綜合考慮來看,雖然團隊的規模可能無法完全确定,但 10 人及 10 人以下的團隊在面臨同樣的限制條件時是可以充分利用這種方法的。
更大規模的團隊也能從這種方法中受益。此外,砍掉一些專用的規則引擎也能受益,除非所有酷炫的核心功能(商用規則引擎主打功能)都是必需的。
結果概述 & 新穎性
下圖列出的步驟描述了這些元件,也展示了該機制下更高層次的功能概覽。

配置存儲了規則,這些規則隻是簡單的 java 方法或布爾表達式。為了避免 Java 文法的冗長,我們也采用了 Lamda 和方法引用,進而在 Java 方法的文法之上提供了一個額外的抽象層。
配置存儲了由領域專家編寫的規則。以下這些都是有效的語句,用類似 lambda 體的文法編寫,以捕獲專家的意圖。例如,要将娛樂軟體評級委員會(ESRB)定級的成人視訊遊戲劃分到合适年齡範圍的産品,領域專家可以這樣寫:
配置可以是一個檔案、原始位元組流或 URL,可以放置在任何地方。放置位置選擇廣泛,如下均可選擇:
一個本地磁盤上的檔案 。
一個資料庫(包括 SQL 和 NoSQL)。
一個遠端網絡位置(如 HTTP URL、原始 TCP 套接字等)。
放置到雲上的一個位置(如亞馬遜雲科技的 S3 bucket 或谷歌的雲硬碟等)
架構提供了對源編碼、安全性和傳輸的全面抽象。任何标準或專有的編解碼器(CODEC)都可以用于存儲和檢索規則,這同樣适用于兩種主要的安全範式 - 機密性和完整性。就這一點而言,每個人都可以編寫自己的擴充卡(Adapter)。隻有這樣,它的檢索過程的最後一步應該産生 Java 表達式或謂語中的規則。
通過另外做一些工作,增加一種機制,這種機制可以使流規則适合采用任何标準協定的浏覽器(如 WebSocket、HTTP/2、XMPP、MQTT)。這種方法甚至可以與 Web-assembly 結合在一起,在 Web 浏覽器中內建安全和複雜的規則。
入門指南
雖然傳統來說,在開始使用 SDK 之前我們最好先了解一下它的結構,但為了友善和簡單,我們颠倒了順序。在本節中,我們将通過簡單的問題陳述或用例來說明規則是如何被外部化的。
先決條件
使用者必須具有使用 Java SE 1.8 或其更高版本進行軟體開發的經驗。除此之外,還需要一台帶有 Java SE 1.8(帶有 IDE)的标準機器。在整篇文章中的所有示例中,IDE 将選用 Eclipse,而建構工具選用 MAVEN。
步驟一
從(https://github.com/trainerpb/easyrule/blob/master/externalize-rules.jar)下載下傳 Library 并将其儲存在本地路徑上,例如D:\ externalize-rules.jar。這裡是完整的源代碼(https://github.com/trainerpb/easyrule)。
步驟二
建立一個 Maven 項目,将編譯器源代碼層級設定為帶有依賴項的 1.8,如圖所示。
問題陳述:一個高檔的服裝零售店是經常在不同的場合為顧客提供折扣,基于一系列不同的可參考标準,比如職業、收入水準、工作 / 居住場所、教育水準等等。為了不複雜,我們假設一個簡單的客戶模型類如下:
我們進一步假定一個類 Repo_Customer,它作為客戶資訊的來源,并根據特定的資格标準挑選符合折扣條件的客戶。
然後,我們可以使用以下任何一種或幾種的組合來定義折扣資格規則:
步驟三
值得注意的是,Lambdas 沒有那麼冗長。主題專家不用深入了解 Java 程式設計語言,就可以使用它們定義規則。
我們将折扣規則保留在代碼庫之外,因為它可能經常更改。然後在所有可用存儲選項中選擇最簡單的一個,将規則存儲在本地磁盤的一個檔案中。SDK 幫助加載和執行這些規則。以下檔案存儲在本地磁盤上:
我們在檔案中建立了自己的标記标準(它可以是任何一個設計和實作,不受任何特定實作的限制):
步驟四
最後,我們寫個小代碼來檢查我們的規則,看看規則是否順利執行:
在 SDK 中
現在,我們已經成功地啟動并編寫了一個簡單的快速啟動程式,我們可以回過頭來開始探索這個 SDK 做了什麼,示意圖如下。
1. 總體方案結構:
1. 從配置源加載原始規則庫
2. 将規則基礎片段合成 java 代碼 (例如 Lamda)
3. 編譯合成 Ja va 代碼
4. 最後将這個類加載到主記憶體中,以便執行規則。
定義架構的具體實作
我們已經在【2】中使用 PredicateRuleExecutor 。這是一個特殊的實作
它執行以下 3 個任務:
1. 加載計算值為 true 或 false 的規則。也就是說,它将原始規則庫或 lambda 表達式轉換為一個 java 方法,該方法接受一個 Object 并傳回一個布爾值,即形成一個
2. 其架構,特别是PredicateExecutionContextLoader,它是RuleExecutionContextLoader的子類,為底層任務提供了抽象。
3. 如果需要加載加密的,編碼的 / 代碼混淆的位元組序列,根據一些标準的 / 定制的編解碼器,必須重寫該方法,
4. 這個方法的一個優點是,它以位元組為機關工作。是以,規則可以從檔案系統、URL、雲存儲、資料庫等地方加載。
我們将簡短說明如何從幾個主要的和常用的源代碼加載規則。
一些通用源代碼的實作
在本節中,我們将探讨一些常見的而且重要的可以加載配置的源檔案。
HTTP 上的遠端 URL
2. 亞馬遜雲科技 S3 bucket
要從 S3 bucket 中加載規則,我們隻需要做以下更改:
把碎片按正确的順序排列
粗略來看,SDK 的實用程式是一個 PoC(Patch Output Converter 成批輸出轉換程式)。然而,創作者們正在努力把它塑造成一個開源項目。在這一節,也就是文章的最後一部分,我們會着重強調它的一些顯著特征:
該架構可以輕松地與不同的安全架構(如 JASYPT)協作,以確定業務規則的完整性和保密性,是以既不能濫用明文業務規則,也不能注入自己的規則。
因為它隻是普通的 java,是以它非常适合所有流行的架構,例如
Spring and Spring BOOT
JSF
Play
ZK
無論是對于有雲總線的,還是沒有雲總線的 Spring 雲配置(Spring Cloud Config),它都非常适合。
協定中立性使得它可以與任何源代碼協作使用,無論源代碼是部署在雲提供商的雲服務中,還是部署在本地(on-premise)存儲中。
人們可以在任何雲提供商或本地(on-premise)存儲上實作自己的自定義編解碼器。
.我們可以使用 JMX hook 重載已加載的配置,而無需重新啟動應用程式。
作者簡介:
Soham Sengupta 在學術界、研究和産業界有 16 年的工作經驗。作為一名移動計算和網絡技術的碩士,他一直在為 PAYBACK 公司(印度)進行數字化轉型。他是一個真誠的基礎科學崇拜者,狂熱的文學愛好者,充滿激情的程式員,喜歡在社交平台上分享自己的想法,而且這些想法往往被證明是超前的、具有創新性的。如果您想了解更多關于作者的資訊,可以在他的 LinkedIn 上找到。
Srijeeb Roy 在 IT 行業擁有超 23 年的經驗。曾獲印度 Jadavapur 大學的計算機科學與工程學士學位。他過去曾上司 TCS 垂直保險的 Java 基地團隊,目前專注于各種數字技術加速器,這些加速器可以幫助 TCS 客戶加速他們的數字旅程。他在 Java/J2EE/JEE、Spring 技術和混合移動應用架構方面擁有超過 20 年的經驗。他曾經在 Infoworld.com(前身是 JavaWorld.com)上寫過幾篇關于 Java SE、EE 和 ME 的文章。
檢視英文原文:
https://www.infoq.com/articles/java-external-rules-engine/