BRMS（ルールエンジン）を使って爆速でAPIとドキュメント一式を作る

# はじめに
みなさん、BRMS って聞いたことがありますか？私は７～８年ほど前にこういうモノがあることを知って興味を持っていたのですが、なんだか難しそう・・・という先入観があり今まで放置していました＼(^o^)／

重い腰を上げて調べてみたところ、あれ？これって・・・メチャメチャ良いやん！！って感じたので、記事にしてみました。

（実際にモノを作るのは爆速ですが、記事が長文になってしまったことを最初にお詫びさせて頂きます・・・🙇‍♂️）

# BRMS とは
BRMS とは Business Rules Management System の略で、ビジネスルールを管理するシステムです。ビジネスルールとは業務で決まっている規則や判断基準のことで、これらを BRMS に定義して、業務上の判断をさせてしまおうという目論見です（なるほど、わからん）。

詳細は Google 先生に譲るとして、これを導入することで何が美味しいかというと、以下のような恩恵を受けられます。

* ローコードで開発できる
* ロジックが分離できる
* 変更に強くできる
* 可搬性を実現できる
* 各種ドキュメントを自動で生成できる
* ユーザーにも分かりやすい（見える化）

BRMS にも色々な製品があるのですが、今回はオープンソースで手軽に利用できる Kogito を使って動きを見て行こうと思います。ちなみに Kogito には BRMS 以外にも BPMS や Planner など色々な機能がありますので、機会があれば（勉強して🙄）その記事も書いてみたいと思います。

https://kogito.kie.org/

また Kogito の BRMS のエンジンには [Drools](https://www.drools.org/) というオープンソースのルールエンジンが採用されているのですが、Redhat 社が商用サポートを付けて Red Hat Decision Manager として製品化をしています。

本家のドキュメントよりこちらの方が分かりやすくまとめられていたので、ありがたく使わせていただきました🙏。

https://www.redhat.com/ja/technologies/jboss-middleware/decision-manager

# 作るもの
今回は Kogito を使って人事評価の判定処理を実装していきます。大まかに実現したい処理は以下の通りです。シンプルですが分かりやすさ重視で行きます！

* 当期評価を複数の評価軸で入力する
* 評価軸の平均点を算出する
* 平均点に応じた当期総合評価を判定する
* 前期総合評価と当期総合評価の組み合わせで昇降格を判定する

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1036913/0ef4929e-60bb-3648-6811-b5d58e251fe3.png)

# 環境など
Kogito は Java で動くのですが、Java で開発といえばまずは Eclipse ですよね・・・という時代でもないので、今回は VSCode を使っていきます。OS は Windows 10 で開発環境は下記の通りです。

* OpenJDK version 11.0.12 （Corretto）
* Apache Maven 3.8.2
* Kogito 1.10.0.Final

VSCode の Plugin は下記の通りです。

* Extension Pack for Java
* Kogito Bundle

インストールや設定で困るところは特にないと思いますが、環境変数の設定は漏らしやすい部分なので、それぞれの手順に従ってしっかり設定しておいてください。また、Kogito 自体はこの後の手順で Maven がよしなにやってくれるので、何もしなくて大丈夫です。

# プロジェクト作成
Maven を使ってプロジェクトの雛形を作ります。Kogito は Spring-Boot や Quarkus 上で動作するのですが、今回は軽量で動作が速く Live Reload など開発に便利な機能も持っている Quarkus を使ってみようと思います。

任意のディレクトリで、以下のコマンドを１行にして投入してください。尚、`groupId`と`artifactId`は好みに応じて変更してください。

```powershell:powershell（１行にして投入）
mvn archetype:generate
   -DgroupId="com.mycompany.app"
   -DartifactId="my-app-id"
   -DarchetypeGroupId="org.kie.kogito"
   -DarchetypeArtifactId="kogito-quarkus-archetype"
   -DarchetypeVersion="1.10.0.Final"
   -DinteractiveMode=false
```
実行が完了すると以下のような構成でプロジェクトが作成されます。フォルダ構造もシンプルで、最小限のファイルしか存在しなので非常にスッキリしています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1036913/a7f4c036-a65d-979d-3d72-91b5b6ba1290.png)
では、ここで軽く動作確認をしておきましょう。次のコマンドでサーバーを起動できます。

```powershell:powershell
mvn clean quarkus:dev
```
サーバーが立ち上がったら`http://localhost:8080`にアクセスしてみてください。Kogito のウェルカムページが表示されればセットアップは無事完了です。

ちなみに、画面右上に Swagger UI のリンクがあるので、そこからサンプルアプリケーションの API を試すことができます。

# 意思決定要件ダイアグラム作成
事前準備はできたので、早速処理を作っていきましょう。Kogito ではビジネスルールを意思決定要件ダイアグラム（Decision Model and Notation）に定義していきます。この辺りから本格的に BRMS の世界に入っていきます。

まずは`src\main\resources`下に`Evaluation.dmn`というファイルを作ります。これを開くと下のようなエディタが開くはずです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1036913/2e6ac7d6-1539-9f88-2a76-b391f0457486.png)

## データタイプ定義
ビジネスルールの定義から行いたくなるところですが、ここは気持ちをぐっと抑えてデータタイプから定義していきましょう。データタイプを定義することで処理の見通しが圧倒的に良くなります。

データタイプのタブ（①）を選択し、新規のデータタイプ（②）を追加していきます。項目の編集は各アイコン（③）を使って行っていきます。

初期表示は若干画面イメージが違いますが、特に迷うところはないと思います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1036913/62725c5b-6a4d-d9c8-7ead-f3397ce44f89.png)
こんな感じで、今回の判定処理でインプットとなる「当期評価」と「前期総合評価」のデータ構造を定義していきます。データには取りうる値の範囲で制約を掛けることができるので、ここで設定しておきます。

:::note info
項目名を日本語で設定していますが、ここで定義した名称は REST API のパラメータ名や 自動生成されるドキュメントに使用されるので、日本語にせよ英語にせよユーザーの用語に合わせるなど、分かりやすい名前で付けておくことがポイントです。
:::

## ビジネスルール定義
さて、いよいよ本丸ですね。ここでは以下のような定義を作成していきます。

はい、そうです。冒頭部分に載せた処理フローが実はビジネスルール定義そのものだったんです。BRMSを今回初めて知った人も、おおよそどの様な処理が行われているかすぐに理解できたのではないでしょうか。このように誰が見ても分かりやすいというのが BRMS の優れているところです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1036913/dc30417b-cbe6-c358-920f-ec373054c718.png)
一応簡単に説明しておくと、楕円が外部からのインプットを表し、長方形がデシジョン（処理）を表しています。そして矢印は、その処理が何をインプットとして受け取るか、アウトプットをどのデシジョンに渡すかを表しています。

### インプット定義
ではインプットの定義から行っていきます。

まずは、エディタの左上にあるインプットの図形を選択して適当な場所に配置します（①）。次に、そのオブジェクトを選択した状態で右上のプロパティボタンをクリックします（②）。そしてオブジェクトに名前をつける（③）とともに、データタイプに先程定義した「当期評価」を設定します（④）。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1036913/4707172b-b449-7f62-bef6-851e99a0d0e7.png)
「前期総合評価」も同じ要領で配置と設定を行ってください。特に難しいところは無いと思います。

### デシジョン定義
こちらもインプット定義同様にエディタ左上の長方形の図形を選択して適当な場所に配置し、名前をつけます。

次に、そのオブジェクトを選択した時に表示される「編集」アイコン（①）をクリックし処理の編集画面を開きます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1036913/7a87c45f-c093-2f6e-4da7-5f7738c3a453.png)
編集画面が表示されたら、式の選択（①）をクリックし、論理タイプに文字式（②）を選択します。論理タイプは簡単にいうと処理タイプを表しているのですが、色々なタイプが用意されているので詳しくは [リンク先](https://access.redhat.com/documentation/ja-jp/red_hat_decision_manager/7.11/html/developing_decision_services_in_red_hat_decision_manager/dmn-boxed-expressions-con_dmn-models) を読んでみてください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1036913/416b94f9-d75c-75f5-8f25-1ac6d31c30cf.png)
論理タイプを選択すると一行一列のテーブルが表示されるので、ヘッダ部分（①）をクリックし、データ型に number（②）を選択します。これはこの処理の戻り値のデータ型を表します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1036913/fc65e50f-84be-a1f6-87e7-bb48bd99c1d7.png)
次に、テーブルのデータ部分（①）に以下のような記述を行います。これが処理の本体なのですが、FEEL 式という言語で記述する必要があります。

```:FEEL式（評価軸の平均を算出して整数に丸める）
decimal(mean(当期評価.評価軸１,当期評価.評価軸２,当期評価.評価軸３),0)
```
変数名に半角スペースを含められるちょっと変わった仕様を持つ言語なのですが、自然言語で記述できるように工夫されており、ユーザーでも理解しやすくなっています。

また、詳しくは [リンク先](https://access.redhat.com/documentation/ja-jp/red_hat_decision_manager/7.11/html/developing_decision_services_in_red_hat_decision_manager/dmn-feel-con_dmn-models) を見てほしいのですが、FEEL 式には比較的簡単な関数しか用意されていないので、設計段階で入出力のデータ構造を単純化しておいたり、複雑な処理は Java で実装（論理タイプを関数とする）するなどの工夫をしたほうが良さそうです。

入力データへは、矢印の起点のオブジェクト名とそのデータ型のプロパティ名を「.」で繋げて指定することでアクセスできます。この辺りは特に違和感はなく受け入れられると思います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1036913/d33d00b6-281f-5a69-b5b2-7599f2608e5a.png)
同じ要領で「当期総合評価」と「昇降格判定」を作成していくのですが、大きな違いは論理タイプを「デシジョンテーブル」としている点です。

デシジョンテーブルは、薄い水色の列（①）が入力値の状態を表し、濃い水色の列（②）がその状態のときの判定結果（出力値）を表す構造となっています。極めて直感的で分かりやすいですよね。

これを踏まえ、「当期総合評価」は下の図の通り定義します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1036913/fa774d6c-83dc-43f4-0a05-2a7e20a74170.png)
同様に「昇降格判定」を下の図の通り定義します。「当期総合評価」との違いはヒットポリシーを FIRST としたところです。

「当期総合評価」のように、判定条件に重複がない場合は UNIQUE を指定します。UNIQUE は定義に重複があるとエラーとしてくれるので、フールプルーフとしても役立ちます。

一方の FIRST は最初に条件を満たした時点で判定処理を抜ける動きとなるので、ワイルドカード（ハイフン）を使って ELSE 条件を表現する場合に便利です。全ての組み合わせを定義して UNIQUE として表現することも可能ですが、現実的ではないですよね。

ヒットポリシーの詳細については [こちら](https://access.redhat.com/documentation/ja-jp/red_hat_decision_manager/7.11/html/developing_decision_services_in_red_hat_decision_manager/dmn-boxed-expressions-con_dmn-models#dmn-decision-tables-hit-policies-con_dmn-models) を参照してください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1036913/c32e8fe8-22c2-e208-9e9e-75c4369efb46.png)
これでビジネスルールの定義は終わりです！簡単でしたよね！ 👀ｼﾞｰｯ
# 設計書作成
意思決定要件ダイアグラムの作成ができたので、このタイミングで設計書を作ってしまいましょう。ここまでの手順でエディタ上部にドキュメントのタブ（①）があることにお気づきだったでしょうか？
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1036913/6d7dd69c-0f2f-64cd-d2e6-7d62eb58f2aa.png)
はい、これを選択するだけで設計書が自動生成されます。

自動生成されると言っても、これまでの手順で作成した定義がきれいにレイアウトされて出力されるだけなのですが、元々 BRMS はユーザーが見ても分かりやすいように工夫がされているので、これで事足りると思います。

もしこれで不足があるようであれば、ビジネスルールやデシジョンテーブルにもコメントが記載ができますし、ビジネスルールの根拠となる規定書やガイドへのリンクも追加できるので、これらを使って補うこともできます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1036913/be03cd98-a49b-2133-5bb2-dff15116ac3d.png)

# 動作確認
では、動作確認をしていきましょう！

環境構築のときと同様にサーバーを起動して `http://localhost:8080/q/swagger-ui/` にアクセスします。

```powershell:powershell
mvn clean quarkus:dev
```
ここまでで作成したのは意思決定要件ダイアグラムだけなのですが、Kogito が REST API のエンドポイントと、Swagger 上に API 仕様書まで作成してくれたことにお気づきいただけましたか？

ど、どえらいことやで・・・
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1036913/221e339b-4536-9e83-d311-2e2cd0d48ea4.png)
では、せっかくなので Swagger を使ってAPIを叩いてみましょう。`/Evaluation` のブロックを展開して、「Try it out」をクリックして、実行してみます。

おや、エラーコード 500 が返ってきていますね。Response body を見ると何やら文字化けしたメッセージが記載されています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1036913/af5c7299-7221-5518-e205-fb7e780ae6cc.png)
この文字化けの仕方・・・( ﾟдﾟ)ﾊｯ!

UTF-8 でエンコードされた文字列を Shift-JIS でデコードした時に見られる文字化けの特徴が出ているので、おそらく Kogito が `Evaluation.dmn` を読みに行く時に Shift-JIS（MS932）で読み込んでいるのが原因ですね。

私くらいの経験者になると、このくらいはピンと来るようになります。
はい、嘘です。少しハマりました・・・。

ファイルを Shift-JIS で読んでいる理由としては Windows のデフォルトエンコーディング設定くらいしか思い当たらないので、環境変数をセットしてみましょう。

```powershell
$env:MAVEN_OPTS="-Dfile.encoding=UTF-8"
```
サーバーを再起動したらもう一度トライしてみましょう。
今度はバッチリ動きましたね！判定も正しく行われていそうです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1036913/27ecabf2-331a-97a9-f436-68a5613e4eb2.png)
ちなみに、dmn ファイルは XML 形式でデータを保持しているので、エンコードの指定なども行ってみましたが期待した動きにはなりませんでした。もし、もっとスマートな解決方法を知っている人がいれば教えて下さい。

# テスト自動化
人間とは欲深いもので、ここまで実現できるとテストも自動化したくなりますよね。次はその部分を見ていきましょう。

## 事前準備
まずはテストの自動化のために依存モジュールを１つ追加します。`pom.xml` の `dependencies` 下に以下の要素を追加してください。

```xml:pom.xml
<dependency>
  <groupId>org.kie.kogito</groupId>
  <artifactId>kogito-scenario-simulation</artifactId>
  <scope>test</scope>
</dependency>
```

追加したら、`src\test\java\testscenario` 下に `KogitoScenarioJunitActivatorTest.java` ファイルを作成します。ファイルパスは重要なので間違えないようにしてください。

このクラスは、これから作成するテストシナリオを JUnit に認識させる役割を果たします。ソースは以下の通りで、アノテーションが指定されているだけで中身は空っぽです。

```java:KogitoScenarioJunitActivatorTest.java
package testscenario;

@org.junit.runner.RunWith(org.kogito.scenariosimulation.runner.KogitoJunitActivator.class)
public class KogitoScenarioJunitActivatorTest {

}
```
事前準備は以上です。この辺りはお作法だと割り切って設定してしまいましょう。

## テストシナリオ作成
テストシナリオは `src\test\resources` 下に `Evaluation.scesim` というファイルを作成して定義していきます。こちらもファイルパスと拡張子が重要なので間違えないようにしてください。

ファイルを開くと簡単なウィザードが立ち上がります。今回は DMN 形式でビジネスルールを定義しているのでソースタイプに DMN（①）を選択し、テスト対象に Evaluation.dmn（②）を選択して Create ボタン（③）をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1036913/0b7a84bc-dfe0-5c08-5f91-ad0cb7399111.png)
すると、こんな感じで画面が開きます。ここでテンションが爆上がりしたのは私だけでしょうか・・・。

ここから先は何をすればよいか直感的に分かりますよね。GIVEN にテスト条件を入力し、 EXPECT にその際の期待値を入力するだけです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1036913/1c57d808-cf3c-7058-b84f-05f969f43389.png)
こんな感じです。ちなみに、このテストケースは抜け漏れだらけなので、良い子の皆さんはきちんとケースを洗い出しましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1036913/35a9cd98-3502-17ca-00bc-08b9f1b25972.png)
テストケースが完成したら、テストを流しましょう。VSCode のテストタブ（①）を選択し、testscenario 横の実行ボタン（②）を押すだけです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1036913/cde0bcec-7f47-051f-d419-f9d1cab9c1b9.png)
結果はこんな感じです（ニンマリ）。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1036913/241b0aff-f9bc-cc16-d157-3dd3afd932c7.png)
以上です！！

# まとめ
環境構築からテストまで、駆け足で説明してきましたがいかがだったでしょうか？BRMS という言葉は初耳だったかもしれませんが、便利そうだなと思っていただけたら幸いです。

ビジネスルールを理解しやすい形で定義できること自体メリットなのですが、ドキュメント類を自動生成してくれたり、テストが簡単にできたり、様々な工夫がされていて非常に便利だと感じました。

また、途中で作成した意思決定要件ダイアグラムは Object Management Group という団体によって仕様が標準化されていて、ロックインされないところも地味に良いなと感じました。

ちなみに、ちょっと調べた感じだと [Camunda](https://camunda.com/) などがこの仕様をサポートしていました。

https://www.omg.org/dmn/

一方で、日本語ドキュメントが不足していたり、エディタのテーブル編集機能の操作性（特にコピペ、連続データ登録）などにはまだ課題があると感じました。

BRMS という考え方が出てきてから久しいですが、そろそろ日の目を見る時代がやってくる気がしました！

# 参考リンク
当記事を書くに際して @Erina さんの投稿を大いに参考にさせていただきました！
ありがとうございました🙏

https://qiita.com/Erina/items/72933529e61f7de7dc5e