Camunda BPMでBPMNなワークフローを作ってみる

BPMN表記法に準拠したワークフローツールであるCamunda BPMを使い始めました。
日本語のドキュメントがほとんどなく、ある程度使えるようになるのに一苦労したので、インストールやシンプルなワークフロー作成といった基本的な使い方を中心に整理しました。

(参考：公式ドキュメント)
https://docs.camunda.org/manual/

# Camunda BPMとは

ビジネスプロセスの可視化や自動化を行うオープンソースなワークフローツールで、BPMN表記法(*)に従ったモデリングが可能な点が特徴です。
ビジネスロジックが標準的な記法で可視化されることで、効率よくノウハウ共有できることがメリットとして挙げられます。

(*)OMG(Object Management Group)で標準化が進められているビジネスプロセスの表記法。

Camunda BPMは主に２つのツールで構成されます。

| ツール名       | 何をするか？ | 動作形態 |
|:-----------------|:------------------|:------------------|
| Camunda Modeler  | BPMN表記方に従ったワークフローを作成する | WindowsやMacのデスクトップアプリ |
| Camunda BPM Platform | 作成したワークフローをプロセスとして実行する | Dockerコンテナ   |


# 環境構成

本記事では、1台のMac Mini(2014)上にインストールしました。
各種バージョンは以下のとおり。

+ OS: macOS Mojave 10.14.5
+ Docker: v19.03.2
+ Node.js: v12.11.1
+ Camunda Modeler: v3.3.5
+ Camunda BPM Platform: v7.11

### 環境構築イメージ
![CamundaBPM.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/94561/c0ff6cae-fab2-22cd-c969-caecc0de44d5.png)
インストール後は①②③の順番でワークフローを作成〜実行します。


# 1. インストール

## 1.1 Camunda BPMのインストール

公式ドキュメントに従い、Dockerコンテナを起動します。

```
$ docker pull camunda/camunda-bpm-platform:latest
$ docker run -d --name camunda -p 8080:8080 camunda/camunda-bpm-platform:latest
```

## 1.2 Camunda Modelerのインストール

ブラウザから[ダウンロードページ](https://camunda.com/download/modeler/?__hstc=12929896.996e749cbb0b97eb3451b64709445af6.1570457291230.1570868637475.1570927139197.7&__hssc=12929896.6.1570927139197&__hsfp=3121342513)にアクセスし、Open Source Modelerの圧縮ファイルをダウンロードします。

![downloadpage.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/94561/aee9cff4-8e11-642b-e50b-deea1bebc5f6.png)

ダウンロード後、適当な場所に圧縮ファイルを解凍すればインストール完了です。


# 2. シンプルなワークフローの作成

入力したパラメータを加工し出力するシンプルなワークフローを作成してみます。
ワークフローに以下の機能を追加していきます。

+ パラメータの入力フォーム
+ パラメータを加工するサービスロジック(node.js)
+ 加工されたパラメータを確認するための出力画面


## 2.1 モデリング

まずはワークフローの流れをモデリングします。
Camunda Modelerを起動後、画面中央の「BPMN diagram」ボタンを押します。

エディット画面が表示されたら、左上の部品メニューから部品をドラッグ＆ドロップし、以下の構成になるように部品を配置します。
![modeler01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/94561/f66264c9-3306-a4c2-c687-03c99aeccedb.png)

流れがわかりやすくなるよう、それぞれの部品をダブルクリックし、左から「開始」「パラメータ加工」「結果確認」「正常終了」と入力します。
![modeler02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/94561/52f32696-ab3a-7271-89df-62258cab0a49.png)

パラメータの入力フォームを作成します。
「開始」を選択し、画面右側のプロパティパネルにて「Forms」タブを選択します。
Form　Fieldsの「＋」ボタンを押すとパラメータが追加されるので、ID、Type、Labelを「testValue」「long」「Test Value」と設定します。
入力パラメータを追加したことで、プロセス開始時にパラメータ入力を求める画面が表示されるようになります。
![modeler03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/94561/f552e41d-e2db-bb3c-da08-1b4bbaded915.png)

続いて、「パラメータ加工」タスクを設定します。
「パラメータ加工」タスクを選択し、表示されるスパナアイコンをクリック、タスク一覧から「Service Task」を選択します。
![modeler04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/94561/48963d17-6009-4f23-9c91-34ac740c10c9.png)

画面右側のプロパティパネルにて、Implementationに「External」を選択、Topicに「testTopic」と入力します。
これは後ほど作成する外部のサービスロジックと連携するための設定で、testTopicを経由してサービスロジックとの間で処理の受け渡しができるようになります。
![modeler05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/94561/98add4e0-b85e-b6f9-7f0b-19c7a50f2730.png)

結果確認タスクを設定します。
先ほどと同じように「結果確認」タスクを選択、スパナアイコンから「User Task」を選びます。
画面右側のプロパティパネルにて、Assigneeに「demo」と入力します。
こうすることで、demoユーザへ承諾を求めるようになります。
（demoユーザが承諾するまでワークフローが中断する）
![modeler06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/94561/be4a1fc7-744b-8afa-028e-7c9b2e965fc3.png)

これでワークフローが完成です。
最後にこのワークフローを識別しやすくするためにidと名前を設定します。
エディット画面の何もない部分をクリックした状態で、画面右側のプロパティパネルのidとNameをそれぞれ「test」「Test」に設定します。
![modeler08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/94561/9745107b-aedd-da8f-27da-f34d93430e95.png)

## 2.2 デプロイ

作成したワークフローを、Camunda BPM Platformへデプロイします。
ツールボックス一番右の「Deploy Current Diagram」ボタンを押し、表示されたダイアログ上のDeployボタンを押します。
Successfulのメッセージが表示されたらデプロイ成功です。
![modeler07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/94561/500b76a6-4aac-8681-237d-0902fde4fb8e.png)

## 2.3 サービスロジックの作成

まずはnode.jsの実行環境を作成します。

```
$ mkdir test-worker
$ cd test-worker
$ npm init -y
$ npm install -s camunda-external-task-client-js
```

サービスロジックのコードを作成します。
testTopicを定期的にポーリングし、プロセスからタスクが格納されたら加工処理（入力されたパラメータに100を加算）を行います。

```
$ vi worker.js
```

```javascript:worker.js
const {Client, logger, Variables} = require('camunda-external-task-client-js');

const config = {baseUrl: 'http://localhost:8080/engine-rest', use: logger, asyncResponseTimeout: 10000};
const client = new Client(config);

client.subscribe('testTopic', async function({task, taskService}) {

  // 入力パラメータを取得する
  const testValue = task.variables.get('testValue');

  // パラメータを加工する
  const processVariables = new Variables();
  processVariables.set('processedValue', testValue + 100);
  processVariables.set('ProcessedDate', new Date());

  console.log("Process Successful");

  // 処理完了
  await taskService.complete(task, processVariables);
});
```

サービスロジックを実行します。
実行すると、タスク格納の待ち受け中状態になります。

```
$ node worker.js
```



## 2.4 プロセスの実行

ブラウザからCamunda Tasklistにログインします。
http://localhost:8080/camunda/app/tasklist/

ユーザ名/パスワードは、デフォルトのdemo/demoを入力します。

ログイン後、画面右上の「Start process」ボタンをクリックします。
![tasklist01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/94561/51860439-97fe-bfed-db7e-c9cac0cb8a1a.png)

プロセス一覧が表示されるので、先ほど作成した「Test」を選択します。
パラメータの入力フォームが表示されたら、適当な数字を入力して「Start」を押します。
![tasklist02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/94561/00625ce2-bd75-5f14-1414-bb5b5fe251dd.png)

実行すると、右下に「結果確認」の通知が表示されるので、通知をクリックすると「結果確認」画面に切り替わります。
「Load Variables」リンクをクリックすると、パラメータ一覧が表示されます。
入力パラメータに加え、サービスロジックで追加された変数「processedDate」「processedValue」の値が正しく表示されていればOKです。

![tasklist03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/94561/9ffefa20-166f-c7bf-ff51-895a7b0667a0.png)

またDiagramタブを選択すると、現在のタスクがワークフローのどの位置にいるのかも確認できます。
![tasklist04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/94561/af7b3b5e-10b0-d143-ff96-a8d6c646126a.png)

最後に「Form」タブの「Complete」ボタンをクリックすると、プロセスが完了します。


# 3. エラーハンドリング

先ほど作成したシンプルなワークフローに、エラーハンドリング処理を追加してみます。
今回は例として、入力された値が100未満ならエラー終了とします。

## 3.1 モデリング

左上の部品メニューから二重丸（Create intermediate）を選び、「パラメータ加工」タスク上にドラッグ＆ドロップします。
さらに二重丸のスパナアイコンから「Error Boundary Event」を選び、エラーイベントに切り替えます。
![modeler09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/94561/8fb5f74e-c87b-beeb-ec16-72cef771f6b6.png)

エラーイベントをダブルクリックし、名前として「例外発生」を入力します。
右側のプロパティ画面の「＋」ボタンを押すとエラーが追加されるので、Error Name、Error Code Variable、Error Message Variableにそれぞれ「ValueError」「errorCode」「errorMessage」と入力します。
errorCodeは、サービスロジックから受け取ったエラーコードの格納先となるパラメータ変数です（errorMessageも同様）。
![modeler10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/94561/87602dca-bef3-3988-d5aa-2cdc204487cc.png)

エラーイベントの後ろに、ユーザータスク「エラー確認」と終了イベント「エラー終了」をつなげます。
「結果確認」タスクと同様に、「エラー確認」タスクのAssigneeにも「demo」を設定します。
![modeler11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/94561/45b53778-71a4-1877-75bd-e57831b900af.png)

## 3.2 デプロイ

先ほどと同じく、ツールボックス一番右の「Deploy Current Diagram」ボタンを押し、表示されたダイアログ上のDeployボタンを押します。
Successfulと出力されるのを確認してください。

## 3.3 サービスロジックの修正

worker.jsにエラー処理を追加します。

```javascript:worker.js
const {Client, logger, Variables} = require('camunda-external-task-client-js');

const config = {baseUrl: 'http://localhost:8080/engine-rest', use: logger, asyncResponseTimeout: 10000};
const client = new Client(config);

client.subscribe('testTopic', async function({task, taskService}) {

  // 入力パラメータを取得する
  const testValue = task.variables.get('testValue');

  // エラー処理
  if (testValue < 100) {
    console.log("Process Failure");
    await taskService.handleBpmnError(task, "-1", "入力パラメータが100未満です。");
    return;
  }

  // パラメータを加工する
  const processVariables = new Variables();

  processVariables.set('processedValue', testValue + 100);
  processVariables.set('ProcessedDate', new Date());

  console.log("Process Successful");

  // 処理完了
  await taskService.complete(task, processVariables);
});
```

サービスロジックを再度実行します。

```
$ node worker.js
```

3.4 プロセスの実行

Camunda TasklistからTestプロセスを実行します。
パラメータの入力フォームで100未満の数字を入力して実行すると、右下に「エラー確認」の通知が表示され、クリックすると「エラー確認」画面に切り替わります。
パラメータ一覧に「errorMessage」「errorCode」が表示されていればOKです。
![tasklist05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/94561/64983f25-ac43-3d1d-b10c-fdb1d85d5005.png)

またDiagramを見ると、エラー確認へ分岐していることがわかります。
![tasklist06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/94561/0f779432-6ec1-4d0e-efc8-9ef44f36fd81.png)

# おわりに

今回紹介した内容以外にもたくさんの機能がCamunda BPMに含まれており、これらを活用すればより柔軟なビジネスロジックのワークフロー化が実現できると思います。
今後も役立ちそうな機能を中心にまとめていきたいと思います。