[レポート] serverlesspressoの開発に見るイベント駆動型アーキテクチャの設計と実装 #AWSreInvent #SVS204

re:Invent 2023で行われたBreakOutセッションBuilding Serverlesspresso: Creating event-driven architectures(SVS204)のセッションレポートをお届けします。
2023.12.11

はじめに

ネクストモードの南です。

re:Invent 2023で行われたBreakOutセッションBuilding Serverlesspresso: Creating event-driven architectures(SVS204)のセッションレポートをお届けします。

セッション概要

Serverlesspresso is an event-driven, serverless workload that uses Amazon EventBridge and AWS Step Functions to coordinate events across microservices and support thousands of orders per day. This session explores the design decisions that were made when building this application, how new features influenced the development process, and lessons learned when creating a production-ready application using this approach. Explore useful patterns and options for extensibility that helped in the design of a robust, scalable solution that costs about one dollar per day to operate. This session includes examples you can apply to your serverless applications and complex architectural challenges for larger applications.

serverlesspressoは、Amazon EventBridgeとAWS Step Functionsを使用してマイクロサービス間のイベントを調整し、1日あたり数千の注文をサポートするイベント駆動型のサーバーレスワークロードです。このセッションでは、このアプリケーションを構築する際に行われた設計上の決定、新機能が開発プロセスにどのような影響を与えたか、そしてこのアプローチを使って本番環境に対応したアプリケーションを作成する際に学んだ教訓を探ります。また、堅牢でスケーラブルなソリューションの設計に役立った、拡張性のための有用なパターンとオプションを探ります。このセッションには、サーバーレスアプリケーションに適用できる例や、大規模アプリケーションの複雑なアーキテクチャの課題も含まれます。

セッション動画

セッション動画はYoutubeで公開されてます。

見どころ

serverlesspressoはドリンクをモバイルオーダーできるインタラクティブなサーバーレスアプリケーションで、re:Invent 2021に登場しました。re:Inventの会場でも店舗のブースが用意されており、serverlesspressoを使って実際に注文することができるようになっていました。(こちらの記事は2022年の様子)

このセッションではserverlesspressoの開発プロセスを通じて、イベント駆動型アーキテクチャの設計手法やアプローチが詳しく解説されます。実際の事例ベースで設計思想や試行錯誤なども交えながら解説がされているので、今後アプリケーションを開発する際の参考になると感じます。

また、余談ですが、re:Invent 2023ではserverlesspressoを実際に構築することができるワークショップが開催されていました。私もこのワークショップに参加していたのですが、せっかくなのでserverlesspressoのアーキテクチャをもっと理解してみようと思い、本セッションを視聴してみました。

レポート内容

アプリケーションの概要

最初にserverlesspressoの概要やワークフローの紹介があります。

  • アプリのワークフロー
    • ブースにいるバリスタの上にあるバーコードをスキャン
    • モバイルデバイスからオーダー
    • バリスタの上にあるスクリーンに注文したメニューが表示
    • バリスタがドリンクを作成開始、作成完了したタイミングで通知が届く
  • 使用しているAWSサービス
    • AWS Amplify consoleでフロントエンドを提供
    • Amazon API Gatewayでフロントエンドとバックエンドを連携し、Cognitoで認証
    • Amazon DynamoDBテーブルにはオーダーを格納するメインテーブルがある
    • AWS IoT Coreでフロントエンドをリアルタイムで最新の状態にリフレッシュしている
    • AWS Lambdaでカスタムコードを実装


アーキテクチャの設計と決定までのプロセス

ここから実際のアプリケーションの開発の場で進められた検討や決定までのプロセスが紹介されます。
まず、最小限のコードによる実装、スケーラビリティ、コスト効率、拡張性を重視するという設計思想のもと、以下のガイドラインを定めました。

  • チームメンバーはそれぞれ1つのコンポーネントに責任を持つ
  • 実装の共有はしない
  • 各マイクロサービスは独自のAPIを持ち、独自のイベントを持つ

続いて、オーダー処理機能の要件を取り決めます。バーコードのスキャン、店が開いているかどうかのチェック、バリスタの定員の取得、キャンセルの処理といった内容です。

オーダー処理機能を実装するにあたり、まずはLambdaでコーディングするアプローチを取りましたが、スケーラビリティに欠けたりコードが複雑になるといった課題が見えてきます。
そこで、AWS Step Functions Workflow Studioで実装する方針に切り替えます。これにより、一時停止や再開、例外などの処理もシンプルに実装することができ、当初のガイドラインに沿った設計ができるようになりました。

この時点でのアーキテクチャは以下のようになります。フロント側はまだ存在しませんが、ゆくゆくはオーダー処理機能とフロントはイベントバスを介してイベントで連携をする形になります。

続いて、オーダー管理機能の検討に入ります。オーダー管理機能の要件としては以下の3点となります。

  • オーダーを更新、キャンセルできる
  • オーダーが完了するまでタスクトークンを保持する
  • 未完了と完了しているオーダーのリストを取得できる

要件を満たすためにいくつかアイディアを検討して、最終的にたどり着いたのがマイクロサービスの形でした。このオーダー管理マイクロサービスはUIと通信するためのREST APIエンドポイントを持ち、イベントをイベントバスに送出します。イベントは右側のオーダー処理のワークフローで処理され、タスクトークンがイベントバスに発行されます。タスクトークンはオーダー管理マイクロサービスのDynamo DBに保管される、という処理が行われます。

初期の実装ではAPI GatewayとLambdaとDynamo DBという構成でしたが、いくつかの改良を加え、最終的にオーダー管理マイクロサービスは以下のStep Functionsのワークフローに置き換えることができました。コーディングを最小限に押さえ、ワークフロー全体のバージョン管理ができ、費用対効果も高いソリューションになっています。

また、フロントエンドをオーダーの状態変化などに合わせて最新化させる仕組みを実装する必要がありました。ポーリング処理などで実装することを検討しましたが、最終的にはAWS IoT Coreを使う方針がベストだと判断しました。 AWS IoT Coreを使えば面倒なWebSocketの管理を任せることができ、多数のフロントエンドがいてもファンアウト処理をサービス側で実行してくれます。

最終的なアーキテクチャとしては以下のような形になりました。
サービス間はイベントで連携されており、疎結合なアーキテクチャになっています。また、注文処理などのワークフローについてはStep Functionsによりオーケストレーションされていることがわかります。

プロジェクトを通して学んだこと

まず、イベントに何を含めるべきか、ということを検討する必要があります。
イベントにおけるsource、detail、detail-typeはすべてプロデューサー側で取り決めることができます。fat eventsとthin eventsのどちらのアプローチで進めるか、バージョニングのような情報を含めるか、という検討ポイントがあります。

イベントバスをデフォルトバスに集約するか、カスタムバスに分離するかという点についても考慮が必要です。デフォルトバスにイベントを送付すれば、他のチームからのアクセス性は上がります。一方、セキュリティなどの理由からバスを分離したい場合はカスタムバスを活用します。

マイクロサービスアーキテクチャにおけるイベントのトラッキングは、チームごとに別々のマイクロサービスを構築し、どのようなイベントが生成されているのかお互いに分からないという状況では課題になりがちです。EventBridgeスキーマレジストリやEventBridge Atlasのようなオープンソースのツールの利用、早い段階からのドキュメンテーションなどの対策が効果的です。

また、マイクロサービス間における設計のポイントも以下のように解説されます。

  • マイクロサービス間は完全に切り離されており、お互いに何も関知しない状態でイベントを発信している
  • また、既存のコードを変更せずに新しいマイクロサービスを追加できる点がイベント駆動型アーキテクチャの強み
  • ただ、全てをイベントで連携させようとすると、余分なイベントやコードを生み出すことになる
  • 一方、Step Functionsを使ったオーケストレーションはリトライやエラー処理に有用だが、注意深く使わないとモノリスを作ってしまう可能性もある
  • イベントによるコレオグラフィとStep Functionsを使ったオーケストレーションの、双方の使い所を見極めるのが重要

有用なパターンとセッションのまとめ

今回偶発的に生まれた、WebSocketを使用したCQRS(Query Responsibility Separation)というパターンが紹介されます。 APIが入力から入ってきて、途中でWebSocket経由で出力されるというパターンですが、状態の更新や何かを通知させたい場合には非常に有用であることが解説されます。また、AWS IoT Coreで実装した今回のアーキテクチャはスケーラブルで費用対効果が高いというメリットもあります。

オーケストレーションとコレオグラフィーの特徴についても改めて整理されます。

また、CRUD処理をStep Functionsで実装する場合のポイントも紹介されました。

  • 1つのAPI Gatewayでワークフローを開始する
  • 最初にCRUDアクションの種類で分岐させる
  • 可能な限りコンピューティング処理を削減し、必要な場合はカスタムロジックにLambdaを使用
  • 戻り値が必要な場合はExpressワークフローを使用

最後に以下のような内容でセッションがまとめられました。

  • デザイン:ワークフローからはじめ、マイクロサービスを加え、フロントエンドを追加する
  • マイクロサービス:イベントとやり取りする。外部向けの場合はAPIを使用
  • リアルタイムフロントエンド:サーバーレスなWebSocketの実装としてAWS IoT Coreを使用
  • Step Functionsを使用し、マイクロサービスの境界内でオーケストレーションを行う。EventBridgeでその間を連携する
  • オーケストレーションとコレオグラフィーを組み合わせることで、拡張性が高く、コードが少なく、コスト効率の高いワークロードを作成できる

おわりに

Building Serverlesspresso: Creating event-driven architectures(SVS204)のセッションレポートをお届けしました。

serverlesspressoの設計思想と開発プロセスを通して、サーバーレスアーキテクチャの強みを活かした設計と、課題と解決方法を理解することができ、今後アプリケーションを開発する際の参考になると感じました。イベント駆動型アーキテクチャやサーバーレスアーキテクチャへの理解を深めることができる良い機会になりました。

ネクストモードについて

ネクストモード株式会社は東日本電信電話株式会社とクラスメソッド株式会社で設立したクラウドカンパニーです。「クラウドであたらしい働き方を」というメッセージを掲げ、さまざまなクラウド技術や製品を組み合わせて企業の働き方の当たり前を変えていくことを目指しています。クラウドやSaaSのご利用に関してお困りごとがあれば、ネクストモードまでぜひお問い合わせください。