Microservicesでサービス境界設計を行うためのドメイン分析

Microservices入門という記事で、

Microservicesを設計する際は、サービスの境界について(つまり、責務の境界について)慎重に検討する必要がある。

と書いた。この記事では、サービス境界を定める手法のひとつであるドメイン分析について書いていく。

サービス境界の設計はMicroservicesをまじめに運用する上でかなり高難易度の課題である。 各サービスは、できるだけ ひとつのこと だけをうまくやるべきだ。 どのようにサービスを分けるか を機械的に設計することは(たぶん)不可能で、ビジネスドメインについて慎重に検討する必要がある。 ここに失敗すると、いびつな依存関係が生まれたり、密結合になったり、クライアントが使いにくいインタフェースになったりする。 データアクセスやメッセージングなどの横のレイヤーでなく、ビジネスドメインに基づいた機能で設計する必要がある。

また、Microservicesは疎結合と高い凝集度を必要とする。 例えば、明確に定義された目的を持つサービスは 高凝集 である。 優れたサービスは、ドメイン知識をカプセル化し、それを抽象化したインタフェースを持つ。 クライアントはサービスの中に持ったアルゴリズムやナレッジを知っていなくても、そのインタフェースから適切な操作を実行できる必要がある。 (ちょうどオブジェクト指向のように。)

このように、ドメインを意識してサービス設計を行うことは、ちょうどDDDのようである。 Microservicesにおけるサービス設計手法のひとつとして、我々はDDD的なアプローチを採用することができる。 DDDにはふたつの異なるフェーズがある。 戦略戦術 だ。

戦略的DDDは、大規模なシステムの構造を定義し、アーキテクチャがビジネス機能と一致していることを維持するために役に立つ。

戦術的DDDは、ドメインモデルの作成に使用するための設計パターンを提供する。

ドメイン分析は以下のように進める。

  • ビジネス用ドメインを分析し、アプリケーションの機能的な要件を理解する。これにより、ドメインの記述の敲きを作成できる。
  • ドメインの境界付けられたコンテキストを定義する。これは、大規模なアプリケーションの中で特定のサブドメインを表すドメインモデルを含んでいる。
  • 境界付けられたコンテキスト内で、戦術的DDDを適用し、エンティティ、集約、ドメインサービスを定義する
  • マイクロサービスを定義する

なお、これには言うまでもなく、エリック・エヴァンスのDDDがベースにある。本を読んでおくといいかもしれない。

以下に詳しく書いていく。

ドメインの分析

コードを記述する前に、作成するシステムを俯瞰する必要がある。DDDでは、最初にビジネス度面をモデリングして、 ドメインモデル を作成する。 まずは、すべてのビジネス機能を洗い出し、またその関係をマッピングする。 関係者が理解できれば、手法はなんでも良い。(ホワイトボードでも、紙でも。)

次に、個別のサブドメインを検討する。

  • このドメインとドメインは密接に関連している
  • このドメインはビジネスの中核である
  • このドメインは付帯的にこのようなサービスを提供している

などを図にすることで、依存関係が可視化される。 また、このとき外部システムとの関係も可視化しておく。支払い、請求システムなど。

このタイミングでは、実装については考慮する必要はない。 とにかくドメインを洗い出すことに集中すれば良い。

分析の流れ

ドメインの分析はいくつかのステップに分かれている。

  • 境界付けられたコンテキスト
  • 集約
  • ドメインサービス

ドメインの分析をなぜやるかというと、最後にMicroservicesに落とし込むためである。 上記のような分析をして、それらをサービスにできるかどうかを検討する、という流れを踏む。

境界付けられたコンテキストを定義する

ドメインモデルには、実際にこの世に存在するものの表現が含まれている。 しかし、それらをすべてのサブシステムが同じ表現で使わなくてもよい。

例えば、商品を出品するためのサブシステムでは、商品の名前、写真、カテゴリなど、様々な情報を表現する必要がある。 しかし、商品を配送するサブシステムでは、商品の重さや配送方法だけを知っていれば良い。 このようなサブシステム群に対してひとつのモデルを作成しようとすると、不必要に複雑になりがちで、モノリスのほうが良かったじゃん。ということになってしまいがちである。 また、それを変更する場合、複数のチームがサブシステムに対して作業を行う必要があり、モデルのアップデートに時間を要することになる。

そこで、現実世界のエンティティをふたつの異なるコンテキストで表すための個別のモデルを設計する。 各モデルは、特定の機能に関連する属性だけを含んでいる。 これにより、各モデルは将来のアップデートが簡単になる。

これは、DDDにおける 境界付けられたコンテキスト ( bounded context )とまさしく同じである。(こちらの記事がわかりやすい。)

また、DDDと同様に、境界付けられたコンテキストを定義したあとはコンテキストマッピングを行っておく。

集約を定義する

境界付けられたコンテキストが定義できたら、集約を見つけていく。 マイクロサービスは、集約よりは大きく、境界付けられたコンテキストよりは小さくすると良いと言われている。

集約(Aggregates)とは、(これもDDDの言葉だが、)1つ以上のエンティティを含んだ一貫性の境界である。 集約の定義は、トランザクションをモデル化することを目的としている。 アプリケーションでは複数のエンティティ(関連オブジェクト)を変更することが多くある。 モノリスでは、それをデータベーストランザクションで実現してきた。 しかし、分散システムでは別サービスにまたがった(つまりに複数のデータストアに対する)トランザクションは不可能である。 ドメインに必要な一貫性は、データレイヤでなくアプリケーションに依存する。

例えば、 商品を発送する という境界付けられたコンテキストについて考える。 商品を発送するときには、

  • 配送履歴テーブルのinsert
  • 商品テーブルのstatusのupdate

のようなフローが必要だとする。(このフェーズは、メンバーでホワイトボードの前で考えるのがいい。) このとき、 配送履歴 商品 というエンティティは 集約 である。 また、これらには当てはまらないような粒度の細かい処理のことを ドメインサービス と呼ぶ。(これもDDD)

ドメインサービスを洗い出す

例えば、配送処理には順番があって、その順番を守る必要があるとする。 順番の保証は、境界付けられたコンテキストでも、エンティティでも集約でもない場合、ドメインサービスとして定義する。

ドメイン分析 まとめ

DDDにおける 境界付けられたコンテキスト 集約 ドメイン分析 は、Microservicesにおけるサービス設計に よく使われる概念である。 次の記事では、これをマイクロサービスに落とし込んで、どうシステムアーキテクチャを設計するかを書いていく。