OSSのルールエンジンでRustコードから呼び出しやすいものがないか探していたところ、ZEN Engine(以下、ZEN)というものに出会いました。
公式のGithubを参考に軽くさわってみたので、紹介していきたいと思います。
ZENとは
Rustで実装されたOSSのルールエンジンです。
GoRulesというコミュニティによって開発されているようです。
2023年8月現在、MITライセンスで公開されています。
Rustだけでなく、PythonやNodeJSのアプリからもライブラリとして利用できるようです。
ルールエンジンに実行させたいルールは、JSON Decision Model というJSONファイル(以下 JDMファイル)の形式で記述します。
サンプルコード
実際にZENを使ってルールを実行するサンプルコードを書いていきたいと思います。
サンプル題材
過去記事と同じ題材ですが、、年齢に応じて飲み物を決定するルールを題材とします。
ルール | 顧客 | ドリンク |
---|---|---|
1 | 年齢が20歳以上 | ビール |
2 | 年齢が20歳未満 | オレンジジュース |
公式のエディターでルールを作成する
先述したように、ZENにロードして実行するためのルールは、JDMというJSONファイルとして記述します。
GoRulesの公式サイトをみると、JDMファイルを簡単に作成するためのエディターが公開されています。
「Edit Graph」を押すと、ルールのInput, Output そしてDecision Table(決定表)を Drag & Dropで追加できます。
ほかにもFunction, Expressionという要素もありますが、ここでは割愛します。
「Confirm」ボタンを押すと、「decisionTableNode」に「Open」ボタンが表示されます。
「Open」ボタンを押すと、Decision Tableを編集するためのタブが表示されます。
このタブ上で、先ほど記載したドリンク決定のためのルールを記述していきます。
ルールの記述が完了しました。
Simulatorを開いてルールの動作検証をすることもできます。
発火したルール行がハイライトされるのでわかりやすいですね。
最後に、「Download json」でJDMファイルをダウンロードできます。
実際に作ったJDMファイルはこちら。
Rustアプリにルールエンジンを組み込んで実行する
作成したJDMファイルを、Rustコード上でロードして実行していきます。
cargoに必要な依存関係を追加していきます。
[dependencies]
zen-engine = "0"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
ルールを記述したJDMファイルのロードと、ルールの実行箇所は以下のようになります。
async fn evaluate() {
// load JDM
let decision_content: DecisionContent = serde_json::from_str(include_str!("DrinkRule.json")).unwrap();
let engine = DecisionEngine::default();
let decision = engine.create_decision(decision_content.into());
// execute rule
let result = decision.evaluate(&json!(
{
"person": {
"age": 21
}
})).await;
println!("Rule Result = {:?}", result);
}
cargo runでRustコードを実行すると、以下のメッセージが出力されます。
Rule Result = Ok(DecisionGraphResponse { performance: "274.4µs", result: Object {"drink": String("Beer")}, trace: None })
問題なくルールが実行されていますね。
Droolsとの違い
公式のブログでDroolsとの比較記事がでていました。
Droolsと比べて、軽量さやシンプルさを打ち出しているようですね。
これからも引き続き注視していきたいと思います。
参考
公式のWEBサイトです
紹介したサンプルコードはこちら