『JapanTaxi』アプリと『MOV』の統合による後継アプリ『GO』の誕生に伴い、決済集計サービスをAWS Step Functionsを利用して開発しました。
サービス統合により言語も仕様も違うリポジトリから作成された様々な決済レコードを集約するのは大変でした。タクシーアプリ『GO』としては初めて決済集計サービスだったため『JapanTaxi』アプリ、『MOV』各々の有識者が集っても正しい仕様が分からない状況が続き、最初の数ヶ月はフワッとした仕様で進まないといけなかったのが大変でした。
また、2つのサービスの総取引を1度で集計するのでデータ量が膨大になり、パフォーマンス設計も同時に必要だったため着手前から課題が多く存在していました。
Step FunctionsはSQSメッセージを送信するのみのシンプルな形にしました。
マイクロサービス側はワーカーを起動させておき、メッセージをポーリングしてメッセージ情報を基に処理を実行する形です。処理完了後、成否をコールバックでSQSに送信してStep Functionsは次のステップに進みます。
設計に関して、
1はDockerイメージID取得のためにマイクロサービスが実行用の情報をAPI公開する必要がありました。「最新のDockerイメージID」は本来アプリケーションの外側の概念なのでアプリケーション自身は意識する必要がないものですが、Step Functionsから実行させるためにアプリケーションの内側から参照する依存関係ができてしまうのがデメリットでした。
3はAWS EventBridgeを導入する必要があり工数が多そうでした。
そのため実装工数が少なくStep Functionsとマイクロサービス間が疎結合になる2を採用しました。
(参考)サービス統合パターン
当たり前ですが、Step Functionsをコンソールからボタン実行もしくは公開APIでキックできるのは便利だと思いました。
ワークフローが未完成なフェーズでは、単純にコンソールから実行していき、開発が進みアプリケーションがワークフローからのパラメーターを受け取れるなど汎用性がでてくる頃には、Step Functionsの公開APIがいい感じに使えるようになってきます。定時実行用に公開APIを利用しても良いです。
MoTのサーバーサイドマイクローサービスはそこまで言語が多様ではないので、Step Functionsのような統合バッチスケジューラーの開発が不可能ではないですが、AWSとワークフロー定義のJSONを管理するだけで統合サービスのトリガーを担保しておけるのは安心でした。
サーバーレスなので、基盤側の障害をほぼ無視できサービス側のアプリケーションエラーのみに専念できる点、ワークフローの更新にデプロイが不要な点も開発スピードの向上に繋がったと思います。
以上のことから、基盤側の信頼性が高く実行のトリガーが安定しているので、実際にデータを流し込み行う結合テストがスムーズに行えた点も良かったとして挙げておきます。
まだ本番稼働して日が浅いですが、感想をいくつか挙げてみます。
・アプリケーションのロジックをワークフロー側に持ち込まないことに専念したほうがいい
Step Functions内でIFを書けるのですが、ロジックはアプリケーション側に寄せたくなるのであまり使わなかったです。並列処理もGoroutineに全部寄せたので利用しなかったです。AWSサービスを経由する場合はLambdaはいいと思いますが、これも同じ理由で処理をマイクロサービスとAWSで分散させると見通しが悪くなってしまうので使わなかったです。なので、テストのしやすさも考えると可能な限りでロジックはアプリケーション側に寄せてワークフローはシンプルな形にした方がいいのではと思いました。
・導入タイミング
STEP毎の処理の成功が担保できていないのに同時にStep Functionsの確認を進めてしまうとエラー発生時の修正のビルドとかで時間とられて大変です。STEP毎の処理の成功を担保してからStep Functionsを導入していくのがよかったのかなと思ってます。
・不要そうなステップが出てくる
途中のステップからやり直したい場合も出てくるので、Step Functionsのパラメーターでステップをスキップする実装があると実行が楽になると思います。
興味のある方は 採用ページ も見ていただけると嬉しいです。
Twitter @mot_techtalk のフォローもよろしくお願いします!