AWS Step Functionsを使いこなそう
何か書くきっかけをもらったので、ネタ帳の中からAWS Step Functionsのメモを引っ張り出して書き起こします。少し前の情報がベースになりますので最新と違っているところがあるかもしれません。
私はServelessなアーキテクチャが好きです。また、ソフトウェア開発者ではありますが、業務ハッカーとして組織の業務改善について考えています。
モチベーション
ソフトウェア開発者である私にとって、AWS Step Functions は非常に便利なサービスでした。当時、組織で働く非ITCメンバーが業務ハッカーの役割をこなすことができないか試行錯誤していた時期でもあり、Step Functionsの恩恵を彼らが享受できると良いと考えました。
そこで、Step FunctionsをBPMNの実行基盤としてラップすることを考えました。BPMN(Business Process Model and Notation)とは、業務プロセスを視覚的に表現するための標準的なモデリング言語です。業務フロー図と言えばこれになります。BPMNを使うことで業務の流れを直感的に理解しやすくなり、プロセスの最適化やリソースの強化に役立ちます。
世の中にはBPMNを動かすことのできるBPMツールやプロセスの一部を自動化できるRPM(Robotic Process Automation)といったDX関連のサービスがたくさん提供されています。しかし少しずつ進めていかなければならない中小企業のDXに対して、どのサービスも初期コストは高く導入には相応の痛みを伴います。
このような課題を抱えた領域に対し、AWS Step Functionsをはじめとしたサーバーレスアーキテクチャを採用することにはわかりやすい大きなメリットがあります。それは効率的なリソース利用による従量課金です。
Step Functionsは特定の業務を迅速に実行するための軽量なワークフローツールとして優れた選択肢となりますが、BPMNのような包括的な業務プロセスを管理するにはステートマシンを工夫する必要があります。
Step FunctionsでBPMNを扱ってみよう
ここからはStep FunctionsでBPMNを扱うために試行錯誤したことを書いていきます。
まずはじめに2つの選択肢を考えました。
ひとつはBPMNのXMLをそのままステートマシン定義のJSONに変換してステートマシンをたくさん生成する方法、もうひとつはXMLをステートマシンの入力として渡して擬似的な動的なステートマシンを定義することです。
当時 Step Functions を使ったことはあったのですが、シンプルな使い方しかしたことがないことがなかったため、BPMNとの互換や関連リソースとの接続を設計しきれませんでした。そのため、はじめから実現性が見えていた動的なステートマシンとして構築していきました。
例として下のような3つのタスクを持ったシンプルなBPMNから考えていきます。
<process id="orderProcess" name="Order Process" isExecutable="true">
<startEvent id="startEvent" name="Start">
<outgoing>flow1</outgoing>
</startEvent>
<sequenceFlow id="flow1" sourceRef="startEvent" targetRef="checkOrder" />
<task id="checkOrder" name="Check Order">
<incoming>flow1</incoming>
<outgoing>flow2</outgoing>
</task>
<sequenceFlow id="flow2" sourceRef="checkOrder" targetRef="processPayment" />
<task id="processPayment" name="Process Payment">
<incoming>flow2</incoming>
<outgoing>flow3</outgoing>
</task>
<sequenceFlow id="flow3" sourceRef="processPayment" targetRef="shipOrder" />
<task id="shipOrder" name="Ship Order">
<incoming>flow3</incoming>
<outgoing>flow4</outgoing>
</task>
<sequenceFlow id="flow4" sourceRef="shipOrder" targetRef="endEvent" />
<endEvent id="endEvent" name="End">
<incoming>flow4</incoming>
</endEvent>
</process>
Stepfunctionsには、入力によりフローを分岐させる Choice が存在します。入力により動的にステートマシンを変更することはできませんが、このChoiceとLambdaを使うことで擬似的に動的な振る舞いをさせることができます。
ステートマシンは動かすBPMNを入力として受け取り、トークンを最初のノードに配置し、ステートマシンに保持します。ステートマシンはsequenceFlowを辿りトークンを次のタスクに移動します。タスクが完了したらステートマシンは再びsequenceFlowを辿り、トークンを次のタスクに移動します。これを繰り返し、最終的にトークンがEndEventに到達した際にステートマシンを終了します。これがBPMN処理のベースとなります。
BPMNオブジェクトの扱いを考える
次に個別にBPMNオブジェクトの扱いを検討していきます。
Activity
アクティビティは何かしら実施すべき作業を表し、業務の完了により次に進むことができます。これはStep Functionsの Run Activity を使うことでタスクの開始、完了を扱います。
Step Functions のタスクは基本的に短時間で行われる自動化できるものを前提としています。しかし人が行う業務というのは数ヶ月かけて行うものもあります。Run Activityの特徴は長時間かけて実行されるタスクを非同期に扱えることです。これはBPMNにおけるActivityを扱うのに適しています。
少し残念なのはStep Functionsがイベント駆動のサービスであるのに対し、Activityの完了判断はポーリングにより実装しなければならないことです。また、Activityには最長期間の制限があることにも注意してください。
Exclusive Gateway
Exclusive Gatewayはいわゆる条件分岐です。業務の内容や周囲の状況などにより次に行うプロセスが変化します。例えば注文の不備や在庫不足の場合には注文をキャンセルするなどです。条件分岐はトークンを動かすLambdaにて条件見て移動先のノードを変更することで実現できます。
Parallel Gateway
Parallel Gatewayは複数のタスクが同時に行われることを表します。BPMNで考えるとゲートParallel Gatewayを通るとトークンが2つに分かれ、合流地点でトークンが揃うのを待ちます。stepfunctions にもParallelフローというオブジェクトが実装されていますが、動的に並列数を決定しなければならないため利用できません。ここでは2つの実装方法を考えます。一つは並列タスクをStepfunctions の Inline Mapで実現することです。Mapは通常繰り返し処理として使われますが、並列での実行が可能です。
ただしこの方法では並列のネスト数に制限が出てきます。試せていませんが、Inline Mapに Stepfunctionsの StartExecutionで自分自身のステートマシンを再帰的に実行することでも実現可能なのではないかと考えています。
まとめ
ここまででBPMNの主要な要素の扱いについては実現できました。BPMNのアップロードやトリガーを設定できるGUIを別途用意してあげる必要はありますが、誰でも bpmn io などで作成したxmlを動かすステートマシンができました。
Step FunctionsはEventBridgeを介して様々な外部サービスとも接続ができます。アクティビティにRunActivityだけではなく、自動化のためのActivityを作ってあげることでさらに便利なBPMNに進化していくでしょう。
一つ気をつけておかなければならないのは、Step Functionsのサービスクオータです。基本的な用途とは異なるため、Activityの登録上限などかかりやすい制限があることに注意してください。(もっと緩和してほしい)
今回BPMNを動かすという内容で書きましたが、Stepfunctionsを使いこなすとフローの組み立て次第でかなり柔軟なワークフローを簡単に設計することができます。使いこなせると日々の業務をもっと良くすることができるようになるでしょう。
そして、一気にDX投資ができない、成果を挙げられない中小企業のためにローコストで始められる強力で従量課金なDXツールが増えることを願っています。地道にできることをがんばっているすべての業務ハッカーのために。
いろいろ試したCDKのコード
Discussion