fbpx

【Event-Driven Architectureへの道】スケールするアプリを作るには? マイクロサービス・ イベントドリブンアーキテクチャで作るべし!

この記事は1年以上前に投稿されました。情報が古い可能性がありますので、ご注意ください。

はじめに

「Event-Driven Architecture」は日本ではまだまだ馴染みが薄い言葉であり、それを取り扱った記事・トピックは非常に少ない現状です。
一方で翻訳されているマイクロサービスの書籍では、イベントドリブンが前提になったデザインパターンが当たり前のように解説され、また以下のようなイベントを見てもアメリカの企業を中心にイベント・ドリブンアーキテクチャの採用が進んでいます。

EDA Summit 2023: https://edasummit.com/event/2023-summit/

なぜ日本では「Event-Driven Architecture」の認知が浸透していないのかは非常に気になるところでまた別の機会に分析したいと思いますが、「Event-Driven Architecture」はクラウドを利用して動的にリソースを拡張可能な環境では非常にスケール可能なアーキテクチャであり、今後エンタープライズシステムも含め、アーキテクチャを大きく変えていくと思われます。

「Event-Driven Architecture」を日本でも広めたい という気持ちから「Event-Driven Architectureへの道」と題し、幾つか記事を書いていくことにしました。

Uber Eats 使ったことありますか?

「Event-Driven Architecture」の紹介を行うにあたりどこから書こうか悩むのですが、筆者が特に紹介したいサービスとして Uber Eatsアプリがあります。
日本では配車サービスの Uber アプリ自体は触る機会がないが、Uber Eats アプリは利用されている方も多いのではないでしょうか。

Uber Eats アプリを初めて触ったときに、これまでの Web アプリケーションとは異なるユーザ体験に正直驚きました。最初勝手が異なりドキドキすることもありますが、ユーザ体験は洗練されており新たな刺激がある一方で、非常に合理的なアーキテクチャになっています。そうユーザ、ドライバーの体験が「イベント」を元に動いている、まさに「Event-Driven Architecture」なのです。

ここで少し、基本的な配達までの流れを見てみると、赤枠の部分だけでも1つのイベントとしての処理が登録できるような部分が理解できるのではないでしょうか。Uber Eats アプリで注文者が注文を入れた後、加盟店がオファーの通知を受諾して、オファーの通知を配達者に通知することもイベントとなります。続いて、商品の準備を開始して、渡せる状態になることもイベントとなり、配達員が商品を受け取り、注文者へ配達に向かうこともイベントではないでしょうか。

このような大量のイベントを「Event-Driven Architecture」で実現する Uber Eats の技術スタックを少し見ていきます。まず、中央には、分散メッセージングシステムの Apache Kafka がテクノロジー スタックの基礎として位置づけられています。プロデューサーは、アプリケーションおよびサービスからイベントをトリガーにメッセージを送信し、コンシューマーは、Kafka に集められたメッセージを処理するアプリケーションです。Uber Eats アプリより注文者が注文を行なった後、配達者(ライダーやドライバ)が順番に確認、処理する背景には、このようなKafka に集められたメッセージをプロデューサーやコンシューマに位置付けられているアプリケーションが処理して、モバイルアプリに対して反映する流れになってます。

一方で Uber Eats アプリを何度か利用する中で、これまでにアプリを利用してきたサービスでは考えにくい体験にも遭遇することがあります。

例えば以下のような体験です。

・Uber Eats で商品を注文して、料理が届くのを待っていてもなかなか届かない…注文したアプリを確認すると、注文が「ご注文はキャンセルされました」という表示が出ている…

・自分でキャンセルした訳でもないし、キャンセルしますか?という連絡がきた訳でもない…自動的に注文がキャンセルされてしまうということが、ごく稀にあるようです。

Uber Eats で、注文が自動的キャンセルされる理由はいくつかありますが、以下の5つが理由として考えられる場合が多いようです。

  • 配達してくれる配達者が見つからない
  • 配達していた配達者が、事故やトラブルなど何かしらの理由で配達できなくなった
  • 配達者が、注文者と連絡が取れなかった
  • 配達者が到着した時点で飲食店が閉店してしまった
  • 特定の食材を切らしている、または大量の注文を受けているなど、レストランが何かしらの理由でお料理を作れなくなった

このように、イベントドリブンが前提になったデザインパターンのサービスだと、これまでのサービス(特に日本)で体験した、常に整合性を重視するサービス(トランザクション制御前提など)と違い、稀にこれまでのサービスでは考えにくい体験が発生することがあります。イベントメッセージは順番に処理していく流れが前提としてあるため、例えば、配達者が注文者と連絡が取れなかった場合は、既に加盟店から商品を受け取って配達しているのだからキャンセルされても仕方なく、「Event-Driven Architecture」での設計としては非常に理にかなっている結果となります。

従来型のアーキテクチャとイベント・ドリブンアーキテクチャ

これまでの記載では、「Event-Driven Architecture」を採用している Uber Eats アプリを見てきましたが、「Event-Driven Architecture」について少しおさらいしてみます。「Event-Driven Architecture」は、アプリケーション設計用の設計パターンの一つで、システム内で発生するイベントに基づいて処理を動作させます。「Event-Driven Architecture」は、コンピューターシステムの設計パターンの一つで、システム内で発生するすべてのイベントに基づいて処理を動作させます。イベントは、アプリケーションなどの状態における何らかの意味のある出来事または状態変化です。イベントの情報ソースは、今回の例でもあるように、「ドライバーにオファーが通知。受諾する」「お店に向かう。お店は商品を準備する」「お店で商品を受け取る」などが該当してきます。「Event-Driven Architecture」の利点でよく語られる内容としては、「スケーラビリティ」「柔軟性」「拡張性」などです。

スケーラビリティ:システムがスケーラブルであることを保証し、必要に応じてリソースを追加可能

柔軟性:非同期設計となるため、、システムは必要な時にジョブを実行可能

拡張性:新しいコンポーネントを簡単に追加できる設計で、稼働中サービスに簡単に拡張可能

「Event-Driven Architecture」では、システム内の複数のコンポーネントが、イベントの発生や処理に基づいて相互作用するので、各コンポーネントは、必要に応じてイベントを発生させ、そのイベントを処理するためのハンドラーを保持することができます。

「Event-Driven Architecture」の利点がイメージできたところで、ここで従来型のアーキテクチャにも触れていきます。

例えば、先ほどの、Uber Eats のようなアプリケーションを作ろうと思ったとき、「Event-Driven Architecture」でない場合は、以下なようなアーキテクチャが思い浮かぶのではないでしょうか。これは、よくモノリスからマイクロサービスアーキテクチャーの移行などで語られる、リクエストリプライによる「リクエスト駆動型アーキテクチャー」の例です。REST API などのリアルタイム連携でサービスを呼び出し、各システムは受動的になります。

リクエスト駆動型アーキテクチャー

メニュー管理、ドライバー管理、決算などは各々独立したサービスであり、各々の責務が明確になっているため、並行開発や機能追加にとっては効率的です。更に、このアーキテクチャには、トランザクション管理する基盤があり、右側のサービス(メニュー管理、ドライバー管理、決算)が提供する各種システムと一貫して連携できるように、注文が入った後のフローを共通 API として提供して整合性をとっています。一方でトランザクションマネージャーの責務が非常に重要になり、プリミティブな動機処理となりスケールしない箇所になる。処理フローの中での障害などによっては、ロックが多発してスケールしずらいシステムになることはご想像の通りです。

当然、以下のようなすべて一つの DB に集約するアーキテクチャもあります。これは単一の大きなシステムが全ての業務を実行してしまう、マイクロサービスへ移行する前のモノリスなアーキテクチャで、全くスケールしないのは既に皆さんも経験しているのではないでしょうか。

モノリス

モノリスの特徴としては、「個別最適が不可で技術スタックがシンプル」「パフォーマンスが劣化しにくい」「結果の一貫性を保証しやすい」などのメリットがある反面、「サービスごとのデプロイが不可」「サービスごとにスケールできない」「障害影響が大きい」などのデメリットがあるのは普段の本番運用などで苦労されていると思います。

Event-Driven Architecture

「Event-Driven Architecture」は、イベント(=メッセージ)を扱い、マイクロサービスで分割されたドメイン毎のサービスに対してイベントで接続していきます。このようなイベントによるマイクロサービスアーキテクチャーは、Apache Kafka などのイベントブローカーを中央に設置して、非同期連携となり各サービス側が能動的に注文のイベントをトリガーとして、各サービスで処理の実行を行います。

結果整合性をイベントドリブンアーキテクチャで実現

先ほどの、リクエスト駆動型アーキテクチャーでトランザクション管理を行う場合に、整合性の担保は非常に難しくなります。例えば、以下のシステムで①〜⑦が正常に処理された場合、⑦番まで実行されて成功が返ります。しかし、実際の全体システムを考えると、ネットワークの障害であったり、依存する先のサービス障害が発生するので、途中で処理が停止した場合、複数のサービスを跨いで一連のトランザクションの整合性を担保するのが非常に難しいです。注文管理システムで注文が行われ、メニュー管理システムで加盟店で調理が始まった状態でも、ドライバー管理システムでドライバーが見つからなかったり、システムそのものが障害になってしまった、決済システムでどのように決済やポイントなどをロールバックするか、など、単純なこちらのシステム図だけでも、どのようにハンドリングしてお客様のお金を正確に管理できるのか、このような分散システムにおける整合性をどのように担保するべきか、難しさを少し分かっていただけるかと思います。

そこで、本記事でも何度も記載している、「Event-Driven Architecture」 のパターンが出てくるわけですが、イベントブローカーに Apache Kafka を利用してイベントを仲介するアーキテクチャーとなります。例えば、ドライバー管理システムの配達状態管理で、ドライバーが見つからずにキャンセルとなった場合は、全体のロールバックを選択する流れです。

ここでもう一つ触れておきたいのが、「Event-Driven Architecture」 のパターンの中に、Saga パターンというのが存在します。Saga パターンは、分散アプリケーションの一貫性を確立し、複数のマイクロサービス間のトランザクションを調整して結果の一貫性を維持するのに役立つ障害管理パターンです。

複数にまたがるサービス毎でトランザクションを分解して、それぞれが独立で処理し長時間ロックしないメリットがある一方で、単純なロールバックができないので、Saga パターンにより複数のトランザクションが関与しているために既にコミットされているものは、単純なロールバックではなく、補償トランザクション一連の逆向きの取り消し操作を行う必要が出てきます。

Saga パターンほか、Event-Driven Architecture のパターン については、次の記事にも記載しているので、ご参照ください。

まとめ

今回の記事では簡略化していますが、Event-Driven Architecture を実際のシステムに導入していくにはシステム設計、ユーザ体験設計、ビジネス設計を含め様々な考慮ポイントがあります。

ただこのようなイベントドリブンなアーキテクチャは非常に堅牢でスケーラブルなサービスを構築することができます。

今回我々が直ぐに体験できる例で Uber Eats を例にしましたが、他にもEventベースで動いているシステムは多数あります。このようなアプリのユーザ体験を通じて EventDriven の設計ポイントを掴んでいくのも一つの方法ではないでしょうか。

現在、レガシーシステム問題と言われる大きな問題があるがこのようなシステムのモダナイゼーションや開発においても「Event-Driven Architecture」は一つの鍵になる と言えるでしょう。

今回の記事が皆さんの「Event-Driven Architecture」の興味や導入のきっかけにつながれば大変嬉しいです。

お気軽にご相談ください

マイクロサービス・イベントドリブンについて
興味関心があり、ご相談があれば
以下よりお問い合わせください

新規CTA