AWS / SAP-C02

SAP-C02-178 · セクション 20 / タスク 4.4

WorkflowをStep FunctionsへModernizeする

AWS Step Functions / State Machine / Workflow / Retry / Error Handling / Orchestration

既存システムでは、複数の業務処理を順番に実行するための制御が、アプリケーションコードへ直接実装されていることがあります。

例えば、

注文受付
↓
在庫確保
↓
決済
↓
承認待ち
↓
出荷

という業務処理に対して、

現在どこまで完了したか
次に何を実行するか
どの条件で分岐するか
失敗時に何回再試行するか
復旧不能ならどこへ進むか

まで専用のアプリケーションが管理している構成です。

このようなシステムでは、業務処理そのものと、Workflowを進行させるOrchestrationが同じコードへ混在していないかを確認します。

1. 独自のWorkflow制御コードを探す

モダナイゼーション候補として確認する代表例が、専用のコーディネーターです。

例えば、

注文コーディネーター

if current_step == "RESERVE":
    在庫確保

if current_step == "PAYMENT":
    決済

if current_step == "APPROVAL":
    承認待ち

if current_step == "SHIP":
    出荷

のように、

現在の処理段階
次の処理
条件分岐

をアプリケーションコード自身が管理している場合があります。

さらに、

current_step
status
retry_count
next_execution_time

などの列を独自テーブルへ保存している場合もあります。

この構成では、新しい処理段階を追加するたびに、

コーディネーターのコード
+
状態管理
+
再開処理

を同時に変更する必要が生じます。

これはAWS Step FunctionsへWorkflowのOrchestration責任を移せる可能性を示す兆候です。

2. State Machineへ移すのは業務処理そのものではなく制御責任

AWS Step FunctionsへModernizeするとき、

在庫を確保するロジック
決済を実行するロジック
出荷を登録するロジック

までState Machineへ書き直す必要はありません。

各サービスには、それぞれの業務責任を残します。

例えば、

State Machine

在庫確保
↓
決済
↓
条件判定
↓
承認待ち
↓
出荷

とし、

在庫確保
→ 在庫サービス

決済
→ 決済サービス

出荷
→ 出荷サービス

のように実際の業務処理は既存サービスへ任せます。

State Machineが担当するのは主に、

どの処理を実行するか
次にどこへ進むか
どこで待つか
失敗時にどう進むか

というOrchestrationです。

3. Workflowの状態をアプリケーションコードから分離する

独自実装では、

業務テーブル
↓
current_step = 3

のような列を使って処理段階を管理している場合があります。

さらに、

定期ジョブ
↓
WAITINGの行を検索
↓
現在の状態を判定
↓
次の処理を呼び出す

という再開機構を自前で実装することがあります。

AWS Step Functionsでは、WorkflowをState Machineとして定義し、各実行がステート間を遷移します。

そのため、

どのステートから
どのステートへ進むか

というWorkflow制御を、業務サービスのコードや独自の進行管理テーブルから分離できます。

重要なのは、業務データとWorkflowの進行状態を同じものとして扱わないことです。

4. 業務データは本来のシステムへ残す

State Machineが状態を管理できるからといって、注文、顧客、在庫、決済記録などの業務データをすべてStep Functionsへ移すわけではありません。

例えば、

注文サービス
→ 注文データを管理

在庫サービス
→ 在庫データを管理

決済サービス
→ 決済結果を管理

State Machine
→ Workflowの進行を管理

と責任を分けます。

State Machineの目的は業務システムのデータベースを置き換えることではなく、複数の処理をどのように進行させるかを明示することです。

5. Retryの独自実装を探す

既存システムでは、一時的な障害へ対応するために、同じような再試行処理が各所へ実装されている場合があります。

例えば、

決済呼び出し
↓
失敗
↓
sleep
↓
再試行

在庫呼び出し
↓
失敗
↓
sleep
↓
再試行

配送呼び出し
↓
失敗
↓
sleep
↓
再試行

です。

しかも、

最大試行回数
待機時間
再試行対象エラー

が処理ごとにばらばらだと、運用や変更が難しくなります。

このような独自の再試行制御は、WorkflowのOrchestration責任としてState Machineへ移せるかを評価します。

6. Retryは「失敗したら何でも繰り返す」設定ではない

Step FunctionsではTaskなどにRetryを定義できます。

ここで確認するのは、

このエラーは
もう一度実行すれば
成功する可能性があるか

です。

例えば、

一時的なサービスエラー
一時的なネットワーク障害
スロットリング

などはRetry候補になる場合があります。

一方、

クレジットカードが拒否された
商品が販売停止になった
承認者が申請を却下した

といった業務結果は、同じ処理を機械的に何度繰り返しても解決しない可能性があります。

したがって、エラーならすべてRetryするとは設計しません。

7. Retryの条件をWorkflow側で明示する

再試行対象である場合は、

どのエラーを対象にするか
何回試すか
どの間隔で試すか

をWorkflow側の方針として定義できます。

例えば、

Task
↓
一時的エラー
↓
Retry
↓
再実行

とします。

これにより、各呼び出し元へ同じ再試行ループを複製する構成から、Workflowのステートごとに再試行方針を明示する構成へ変更できます。

再試行ポリシーがOrchestrationの一部として見えるため、変更箇所も明確になります。

8. Error HandlingではRetryと失敗後の経路を分ける

Retryしても処理できない場合があります。

Step Functionsでは、対応するステートでCatchを使用し、エラー発生後の遷移先を定義できます。

例えば、

決済Task
↓
一時的エラー
↓
Retry
↓
まだ失敗
↓
Catch
↓
補償処理

という形です。

別のWorkflowなら、

Task失敗
↓
Catch
↓
手動確認

という遷移も考えられます。

重要なのは、Retryは同じ処理を再試行し、Catchは解決できないエラーを別の処理経路へ渡すという役割の違いです。

9. ワークフロー全体を最初から再実行する設計を見直す

独自Workflowでは、

ステップ1 成功
↓
ステップ2 成功
↓
ステップ3 失敗

↓

最初から再実行

という実装になっている場合があります。

すると、ステップ1、ステップ2で既に完了した処理まで再度実行される可能性があります。

その処理が、

課金
外部システムへの登録
通知送信

などであれば、単純な再実行が望ましくない場合があります。

モダナイゼーションでは、

どこで失敗したか
↓
その失敗はRetry可能か
↓
解決しなければ
どのError Handlingへ進むか

をWorkflow内で明示します。

10. Error Handlingは業務上の失敗も区別する

技術的なエラーと業務上の結果は分けて扱います。

例えば決済処理で、通信タイムアウトとカード利用拒否は同じではありません。

通信タイムアウトなら、

一時的な障害
↓
Retry候補

になり得ます。

一方、カード利用拒否は、

別の支払い方法を求める
注文を保留する
注文を失敗として終了する

など、業務上の次の処理へ進むべき結果です。

Error Handlingでは、技術的失敗と業務上の結果を区別してWorkflowを設計します。

11. OrchestrationとEvent-driven Architectureを分ける

イベント駆動型アーキテクチャでは、

イベント発生
↓
複数の独立した処理が反応

という構造を取ることがあります。

一方Orchestrationでは、

処理A
↓
処理B
↓
条件分岐
↓
処理C

のように、次に何を実行するか、現在どこまで進んでいるかを明示的に管理します。

したがって、疎結合化したいだけで、すべての処理をState Machineへ入れるわけではありません。

確認するのは、複数処理を1つの業務Workflowとして進行管理する責任が存在するかです。

12. 既存サービスを残したまま段階的にModernizeする

AWS Step FunctionsへのModernizeは、すべての業務サービスを書き直すこととは限りません。

例えば現在、

コーディネーター
↓
在庫サービス
↓
決済サービス
↓
配送サービス

となっているなら、

State Machine
├→ 在庫サービス
├→ 決済サービス
└→ 配送サービス

のように、既存サービスを利用しながらOrchestrationだけを段階的に置き換えることを検討できます。

これにより、業務処理とWorkflow制御を一度に全面再実装する必要を減らせます。

13. モダナイゼーション判断表

既存の状態検討する改善
専用コーディネーターが次の処理をif / elseで決定State Machineへ遷移責任を移せるか評価
current_stepなどを独自テーブルで管理Workflowの進行状態をOrchestrationから分離できるか確認
定期ジョブが停止中の処理を探して再開Workflowの待機・再開責任を見直す
同じRetryループが複数箇所に存在ステート単位のRetryへ集約できるか評価
すべてのエラーを同じ方法で再試行Retry対象と業務上の失敗を分離
エラー時にWorkflow全体を最初から再実行失敗ステート単位のRetry / Error Handlingを評価
エラー処理が各サービスへ分散Catch後の処理経路をWorkflowとして明示できるか確認
業務データとWorkflow進行状態を同じ責任で管理データ責任とOrchestration責任を分離

14. 判断フロー

既存の複数ステップ処理を確認
        ↓
処理順序・現在状態・分岐を
専用コードで管理しているか
        ↓
はい
        ↓
そのコードは
業務処理そのものか
Orchestrationか
        ↓
Orchestration
        ↓
State Machineへ
遷移責任を移せるか評価
        ↓
独自Retryがあるか
        ↓
Retry条件を
ステート単位で明示できるか
        ↓
失敗後の処理が
コード各所へ散らばっているか
        ↓
Error Handlingを
Workflowへ表現できるか
        ↓
既存業務サービスを残しながら
段階的にModernize

最終的に確認するのは、処理順序、状態遷移、Retry、Error Handlingの変更が、各業務サービスのコード変更へ不要に波及しなくなったかです。