yu nkt’s blog

nkty blog

I'm an enterprise software and system architecture. This site dedicates sharing knowledge and know-how about system architecture with me and readers.

イベントドリブンなマイクロサービスアーキテクチャ

背景

会社で、Kafkaを組み込んだシステムをSpringで開発中です。 また、自宅でも、マルチ言語のプログラムを開発中で(JavaPython)、これら二つとも、Spring Cloud Streamが利用出来るのではないか、ということに気づき、調べて使ってみました。

そこで、Spring Cloud Streamについて説明しようと思っていました。 が、補足として、その前提知識であるイベント駆動で連携するマイクロサービスについて書き出したら、結構な量になったため、本日はこれで1エントリとします。

イベント駆動で連携するマイクロサービス

マイクロサービスアーキテクチャでは、大規模なシステムの場合、各マイクロサービスの処理を連携させることがあります。 その際のマイクロサービス間の連携の実現方法には、様々な方法があります。 一般的なのが、メッセージング基盤を用いたイベントやメッセージによる連携です。

マイクロサービスが他のマイクロサービスから完全に独立するためには、マイクロサービス間での処理のコールなどを明示的にコード内に埋め込むのは望ましくありません。 その代わりに、マイクロサービスは、自身が処理をし終えた時や、エラー発生時、その他、何らかのタイミングでイベントを作成します。 このイベントというのは、あくまで、マイクロサービスが他のマイクロサービスを意識せずに、自身の内部の処理または状態に応じて作成するものです。 これにより、あるマイクロサービスが別のマイクロサービスを意識せずに開発を進められるようになります。 なお、具体的な連携方法は、後で述べます。

原則として、イベントの発生元(のオブジェクト)は1つです。 全く同じイベントが複数の異なる発生元から出るように設計してしまっては、イベントを契機に何か処理を行う別のマイクロサービスは、そのイベント信用できなくなります。 また、もし複数のマイクロサービスが同じイベントを出す、という取り決めを作ってしまったら、それこそ依存関係になってしまいます。

イベントストア

マイクロサービスの間には、イベントをストアするイベントストアを用意します。 通常は、Kafkaのような、高スループットが出せるメッセージングキューか、イベントストア専用のDBであるEventStoreやAxonDBを利用します。

EventStore

eventstore.org

AxonDB

https://axoniq.io/product-overview/axondb

これらのようなイベントストアに特化したDBでは、以下の特性を持ちます。

  • 書き換え不可能(イベントとは過去に発生した履歴であるためImmutable(append-onlyとも言う))
  • イベントを、イベントに対する補足情報と共に記録出来る
  • 水平方向の高いスケーラビリティ

イベントによる連携のさせ方

マイクロサービス間の処理の連携方法には、恐らく2通りあります。

連携方法の一つは、マイクロサービスが、他のマイクロサービスが生成したイベントをsubscribeし、イベントに応じて処理をする、という方式です。 これは、マイクロサービスの入力(Controller部分)がイベントになるので、イベントとマイクロサービスの間での依存性が高くならないように、注意してイベントの設計をしておく必要があります。 詳しくは、以下のマイクロソフトの技術ガイドラインをご覧ください。

f:id:yunkt:20181014174610p:plain

docs.microsoft.com

とは言っても、片方がどうしても機能の拡張や縮小によってイベントを修正したい場合もあるでしょう。 その際に参考になるのが、ドメイン駆動設計で語られる腐敗防止層です。 腐敗防止層とは、ドメイン駆動設計における境界づけられたコンテキスト間でイベントを変換する層です。 この層を、マイクロサービスの間にも設置し、しっかり管理することで、それぞれのマイクロサービスが個別に改変出来ます。

もう一つの連携方法は、マイクロサービスの間にプロセスマネージャーなるものを設置することです。 プロセスマネージャーは、どのイベントが発生したらどのマイクロサービスに新たなリクエストを送るかといったルールを実行したり、ステートフルにトランザクションなどを管理します。 これは、比較的見通しが良いアーキテクチャだと思います。 このプロセスマネージャーが、腐敗防止層の役目も果たせば良いでしょう。

しかし、このプロセスマネージャーは少なからず、ドメインロジックを含んでしまうでしょう。 だとすると、このプロセスマネージャーは、ドメイン駆動設計(DDD)でいうと、どのBounded Contextに含まれうるものなのでしょうか?ユビキタス言語として登場しうるものなのでしょうか?などなど、疑問が残ります。 また、誰が(どのチームが)このプロセスマネージャーを開発・保守するのかを考えると、浮いた存在と言わざるを得ません。 さらに、プロセスマネージャーの要件を突き詰めたら、結局SOA (Service-Oriented Architecture)のESB (Enterprise Service Bus)のような重厚長大なものになりかねません。

SOAのESBによるアーキテクチャと、マイクロサービスアーキテクチャの概念的な違いは、以下のRedhatの記事をご参照ください。

jp-redhat.com

非同期による結果整合性

あと、他にここで説明しておくべきは、イベント駆動型で連携するシステムは、基本的には、個々のマイクロサービスが非同期的に処理される事を意味します。 もちろん、コールバックを受けて同期的に処理をさせる場合もありますが、往々にしてイベント駆動なシステムは、非同期処理です。

そのため、システムが扱う(ReadしたりWriteする)データは、基本的にその整合性が、結果整合性になります。 結果整合性については、以下のYahoo!のテックブログの記事をご覧ください。

techblog.yahoo.co.jp

これはつまり、例えば銀行の預金の取り扱いなど、一時でも整合性が崩れてはいけないシステムでは、かなり難しい問題に直面してしまいます。

まとめ

マイクロサービスをドメイン駆動設計における境界づけられたコンテキストに対応して考えるのであれば、ドメイン駆動設計のコンテキストマップで調べると、より多くの示唆が得られる事でしょう。

マイクロサービスアーキテクチャは、各ドメインごとに、開発者がそのドメインに集中して(安心して)、開発を進められるようにする、という方針だと自分は思っています。 そのための手段として、イベントで連携するスタイルが注目されています。 ただ、あくまで手段の一つなので、目の当たりにしている案件に応じて、このスタイルを取るのか、別の連携方法を用いるか、冷静に考えなければなりません。

マイクロサービスアーキテクチャは、「こういう設計をするもの」という手法などではなく、あくまで方針だと自分は思っています。 (ちなみに、マイクロサービスアーキテクチャに対してSOAは、方式かな、と思っています。)