DDDの構成要素とマイクロサービスの単位をどう合わせるべきか

この記事は [ドメイン駆動設計 #1 Advent Calendar 2018](https://qiita.com/advent-calendar/2018/ddd) の 21日目 です。

前日は @mafuyuk さんの「[DDDをチームに導入する際に考慮した4つのこと](https://qiita.com/mafuyuk/items/3dac982d3e7b777e2dce)」でした。
明日は @dnskimo@github さんです。 

# この記事の内容

実務でドメイン駆動設計（以下、DDD）とマイクロサービスアーキテクチャを実践していますが、
DDDとマイクロサービスの粒度について、チームメンバーでの解釈が異なっていることもありました。

この記事では、**DDDの構成要素とマイクロサービスをどう合わせるのがいいのか？** を考察していきたいと思います。

# いきなり結論

先に結論を言ってしまうと、**「DDDのサブドメインをマイクロサービスの単位とする」** になると考えます。
**境界づけられたコンテキスト : サブドメイン（のドメインモデル） : マイクロサービス** が、**1 : 1 : 1** になるのが理想形です。

以降、その理由を解説していきます。

# まずは言葉の定義を

DDD や マイクロサービス の文脈で登場する言葉は受け手の解釈によるところが大きいので、以降の考察がブレないように記事中の言葉を定義しておきます。

## DDD

マイクロサービスとの関係と言う観点では「ドメイン」「サブドメイン」「ユビキタス言語」「境界づけられたコンテキスト」あたりの概念を共通認識できていればOKです。

これらについて、 [ドメイン駆動設計 #2 Advent Calendar 2018](https://qiita.com/advent-calendar/2018/ddd-2) の 18日目の記事 「[DDDのドメイン・サブドメイン・ユビキタス言語・境界づけられたコンテキストを整理する](https://qiita.com/crossroad0201/items/875c5f76ed3794ed56c4)」 で解説していますので、そちらを参照してください。

![domain_subdomain_bc.png](https://qiita-image-store.s3.amazonaws.com/0/94618/7e9bd312-5b88-1936-fcee-a805ef880f35.png)

## マイクロサービス

書籍 「[マイクロサービスアーキテクチャ](https://www.oreilly.co.jp/books/9784873117607/)（以下、Newman）」 を参考に、**「マイクロサービス」** は以下の特徴を持つものとします。

* ある関心事について、その問題を解決するための一連の機能を提供します。
そのマイクロサービスだけで問題が解決まで完結しなければなりません。
* １つの マイクロサービス には、そのサービスを利用するための複数のエンドポイント（RESTful API や メッセージキュー など）が含まれます。
* 他のサービスが停止していても独立して稼働し続けることができます。
その間は他のサービスとの間に不整合が生じえますが、最終的に整合性が取れた状態になります。（結果整合性）
* 他のサービスが稼働したまま、単独でデプロイすることができます。
* マイクロサービスを構成するモジュールは、１つだけのこともあれば複数の場合もあります。
たとえばJVM系言語であれば１つの warモジュール だったり、APIエンドポイントごとの複数の jarモジュール だったりします。

ちなみに、同書では以下のように「マイクロサービスはDDDの境界づけられたコンテキストの単位にすべし」と書かれています。

> 一般に、マイクロサービスは境界づけられたコンテキストときれいに一致するようにします。
> - Newman 第3章 サービスのモデル化方法 / 3.3.2 モジュールとサービス

> 既に述べたように、境界づけられたコンテキストに沿ってサービス境界の線を引くようにします。
> それにより、チームも境界づけられたコンテキストに一致することになります。
> - Newman 第10章 コンウェイの法則とシステム設計 / 10.8 境界づけられたコンテキストとチーム構造

マイクロサービスについての詳細は、以前の記事 「[書籍 マイクロサービスアーキテクチャ まとめ](https://qiita.com/crossroad0201/items/23864b3b177fb1fce966)」 も参照ください。

# DDDとマイクロサービスのパターンについての考察

言葉の定義ができたところで、DDDのどの構成要素をマイクロサービスに対応づけるか？考えていきます。

**マイクロサービスの単位はDDDの設計の結果、つまり「境界づけられたコンテキスト」と「ドメインモデル」がどう分割されたか？が前提となります。**
例えば「ドメイン」が「サブドメイン」に分割されていないのにマイクロサービスにしようとしても、おそらくデメリットだけが強調されてメリットはないでしょう。

下表は DDDの設計の結果 と マイクロサービスの単位 をマトリクスで俯瞰したものです。
以降でそれぞれの組み合わせを見ていきます。

||1.モノリシック|2.マイクロサービス<br/>（境界づけられたコンテキスト単位）|3.マイクロサービス<br/>（サブドメイン単位）|
|----|----|----|----|
|A.サブドメインに分割されていない|⭕️<br/>マイクロサービス化は無理。|❌<br/>マイクロサービスのメリットが活かせない。|❌<br/>マイクロサービスのメリットが活かせない。|
|B.境界づけられたコンテキストに複数のサブドメインがある|⭕️<br/>マイクロサービス化を検討すべし。|❌<br/>１つのマイクロサービスに、複数のサブドメインが混ざってしまう。|⭕️<br/>ただし、成果物が混在してしまうことに注意。|
|C.境界づけられたコンテキストとサブドメインが１対１|⭕️<br/>マイクロサービス化を検討すべし。|⭕️<br/>きれいに一致する|⭕️<br/>きれいに一致する|

## A.ドメインがサブドメインに分割されていない

![figures.draw-1-1-1.png](https://qiita-image-store.s3.amazonaws.com/0/94618/870573cd-7d37-a80a-172c-a21bb0f23f3d.png)

「ドメイン」が「サブドメイン」に分割されていない、つまりドメイン全体が一枚岩（泥団子とも言う）になっているなら、 **そもそもマイクロサービスアーキテクチャを採用すべきではない** です。
「ドメイン」が一枚岩と言ことは、それらの概念は強い整合性を保つべきで結果整合性は採用できないし、一部分だけを単独で発展させていくこともできないはずです。

そのような「ドメイン」を無理にマイクロサービスに分割してしまうと、サービス間でデータの整合性を保つのに苦労したり、複雑な基盤（分散トランザクションとか）が必要になったり、一度に多数のモジュールを同期を取ってデプロイしなければならなくなったり...とデメリットが大きすぎます。
また、ドメインの知識をプロジェクト全体で共有しなければならないので、マイクロサービスにしても組織内での調整・合意形成がボトルネックになります。

無難にモノリシックな構成にするのが良いと思います。

## B.境界づけられたコンテキストに複数のサブドメインがある

![figures.draw-1-1-N.png](https://qiita-image-store.s3.amazonaws.com/0/94618/73da7714-cfa6-207c-7fa2-1f984fa7ebf8.png)

DDDの設計の結果、「ドメイン」が「サブドメイン」に分割されていますが、 **１つの「境界づけられたコンテキスト」の中に複数の「サブドメイン」が含まれている**（つまり、複数の「サブドメイン」でユビキタス言語を共有している）構造です。

このケースでは、マイクロサービスアーキテクチャも採用できますが、**ドメインの構造とマイクロサービスの単位がきれいに一致しない** ので注意が必要です。

マイクロサービスを「境界づけられたコンテキスト」単位に作る場合、１つのマイクロサービスで複数の「サブドメイン」を扱う形になるため **「サブドメイン」を分割した意味が薄れてしまいます。**

例えば、ある「境界づけられたコンテキスト」に「コアドメイン」と「支援サブドメイン」が含まれているとすると、マイクロサービスは「支援サブドメイン」のことを考慮しなければならないため、「コアドメイン」に注力できなくなります。
また、「支援サブドメイン」を変更してデプロイする際、「コアドメイン」の機能も停止しなければならなくなるなどの影響を受けてしまいます。

一方、マイクロサービスを「サブドメイン」単位に作る場合は、それぞれマイクロサービスの独立性・凝集性を確保できます。

ただし、この場合に注意が必要になるのは、成果物（ドキュメントやソフトウェア資産）は「境界づけられたコンテキスト」でまとめられるので、 **１つの「境界づけられたコンテキスト」に複数のマイクロサービスの成果物が混在することになる** 点です。
そのため、構成管理 や 継続的インテグレーション／継続的デリバリ で工夫が必要になるかもしれません。

## C.境界づけられたコンテキストとサブドメインが１対１

![figures.draw-1-N-1.png](https://qiita-image-store.s3.amazonaws.com/0/94618/a3ef96dc-4b01-903b-de5a-1c891f7546a8.png)

DDDの設計の結果、「ドメイン」が「サブドメイン」に分割されており、かつ **「境界づけられたコンテキスト」と「サブドメイン」が１対１に対応している** 構造です。

このケースでは、ドメインの構造とマイクロサービスの単位がきれいに一致します。

なお、１つの「境界づけられたコンテキスト」には１つの「サブドメイン」しか含まれていないので、 **マイクロサービスの単位は「境界づけられたコンテキスト」でも「サブドメイン」でも同義** になります。

# まとめ

以上の考察から、**「DDDのサブドメインをマイクロサービスの単位とする」** のが一番しっくりきます。
これであれば、DDDの設計の結果...つまり「境界づけられたコンテキスト」と「サブドメイン」の関係が１対１なのか、１対多なのか...に関係なく、**マイクロサービスの単位は変わりません。** （前出の図中の `B-3` と `C-3`）

このパターンは、マイクロサービスに関するパターンを紹介しているサイト [microservices.io](https://microservices.io) および 書籍 [Microservice Patterns](https://www.manning.com/books/microservices-patterns) でも [Decompose by subdomain pattern](https://microservices.io/patterns/decomposition/decompose-by-subdomain.html) として紹介されています。

> Define services corresponding to Domain-Driven Design (DDD) subdomains. 

一方、「境界づけられたコンテキストをマイクロサービスの単位とする」と言った場合、**DDDの設計の結果によってマイクロサービスの内容が変わってきてしまうので、解釈の仕方によっては適切なマイクロサービスの単位にならない** 恐れがあると思います。（前出の図中の `B-2` と `C-2`）

なお、前出の 「[マイクロサービスアーキテクチャ](https://www.oreilly.co.jp/books/9784873117607/)」も含めて、多くの文献で「境界づけられたコンテキストをマイクロサービスの単位とする」ことが紹介されてはいます[^1]が、それらはおそらく **「境界づけられたコンテキスト」と「サブドメイン」が１対１になっていることを前提としている** と思われます。
つまり、意図としては「サブドメインをマイクロサービスの単位とする」と同じことを意味していると思います。（前出の図中の `C-2` と `C-3`）

[^1]: 私自身も、この記事を書くまで「マイクロサービスは境界づけられたコンテキスト単位に作るべきだ」と言ってました。

# おわりに

言葉遊びのようなまとめになってしまいましたが、逆に言えばそれだけいろいろな解釈の仕方ができて、曖昧になりやすいとも言えます。
DDDとマイクロサービスアーキテクチャを採用する際は、プロジェクト・チームで認識を合わせておくほうが良いと思います。

また、マイクロサービスアーキテクチャの採用にDDDの実践は必須ではありませんが、疎結合・高凝集なマイクロサービスを導き出すための視点をDDDが与えてくれます。
DDDすべてを実践しなくても、DDDの戦略的設計（ドメインをサブドメインに分割し、ユビキタス言語を境界づけられたコンテキストで区切る...）だけでも導入する価値はあるのではないでしょうか。

# 参考文献

* 書籍 [マイクロサービスアーキテクチャ](https://www.oreilly.co.jp/books/9784873117607/)
* 書籍 [Microservice Patterns](https://www.manning.com/books/microservices-patterns)
* [DDDのドメイン・サブドメイン・ユビキタス言語・境界づけられたコンテキストを整理する](https://qiita.com/crossroad0201/items/875c5f76ed3794ed56c4)
* [Microservices と DDD - 魔女の一撃](http://s-edword.hatenablog.com/entry/2018/12/04/004525)
* [microservices.io](https://microservices.io)


<!--
## https://microservices.io/patterns/decomposition/decompose-by-subdomain.html

Define services corresponding to Domain-Driven Design (DDD) subdomains. DDD refers to the application’s problem space - the business - as the domain. A domain is consists of multiple subdomains. Each subdomain corresponds to a different part of the business.

Subdomains can be classified as follows:

* Core - key differentiator for the business and the most valuable part of the application
* Supporting - related to what the business does but not a differentiator. These can be implemented in-house or outsourced.
* Generic - not specific to the business and are ideally implemented using off the shelf software

ドメイン駆動型設計（DDD）サブドメインに対応するサービスを定義します。DDDはアプリケーションの問題空間（ビジネス）をドメインと呼びます。ドメインは複数のサブドメインで構成されています。各サブドメインは、ビジネスのさまざまな部分に対応しています。

サブドメインは次のように分類できます。

* ビジネスの中核となる主要な差別化要因、およびアプリケーションの最も重要な部分
* サポート - ビジネスが行うことに関連していますが、差別化要因ではありません。これらは、社内でもアウトソーシングでも実装できます。
* 一般的です - ビジネスに固有ではなく、理想的にはシェルフソフトウェアを使用して実装されています

## https://www.manning.com/books/microservices-patterns

2.2.3. Defining services by applying the Decompose by sub-domain pattern

DDDは、Eric Evans（Addison-Wesley Professional、2003）の著書「ドメイン駆動設計」に記載されているように、オブジェクト指向ドメインモデルの開発を中心とする複雑なソフトウェアアプリケーションを構築するアプローチです。ドメインモードは、そのドメイン内の問題を解決するために使用できる形式のドメインに関する情報を取得します。 DDDがユビキタス言語と呼ぶ、チームによって使用される語彙を定義します。ドメインモデルは、アプリケーションの設計と実装に密接に反映されています。 DDDには、マイクロサービスアーキテクチャを適用する際に非常に便利な2つの概念、サブドメインと限定されたコンテキストがあります。

DDDは、企業全体の単一のモデルを作成するエンタープライズモデリングに対する従来のアプローチとはまったく異なります。そのようなモデルでは、たとえば、顧客、注文などの各ビジネスエンティティの単一の定義があります。このようなモデル化の問題は、組織のさまざまな部分を1つのモデルに合意させることが大きな課題であることです。また、組織の所与の部分の観点から、そのモデルは彼らのニーズに対して過度に複雑であることを意味する。さらに、ドメインモデルは、組織のさまざまな部分が異なる概念に同じ用語を使用したり、同じ概念に対して異なる用語を使用したりする可能性があるため、混乱する可能性があります。 DDDは、明示的なスコープを持つ複数のドメインモデルを定義することにより、これらの問題を回避します。

DDDは、各サブドメインの個別のドメインモデルを定義します。サブドメインはドメインの一部であり、アプリケーションの問題空間に対するDDDの言葉です。サブドメインは、ビジネス機能を識別するのと同じアプローチを使用して識別されます。ビジネスを分析し、さまざまな専門分野を特定します。最終的な結果は、ビジネス機能に類似したサブドメインになる可能性が非常に高いです。 FTGOのサブドメインの例には、受注、受注管理、キッチン管理、配達、および金融が含まれます。ご覧のとおり、これらのサブドメインは、前述のビジネス機能と非常によく似ています。

DDDは、ドメインモデルの範囲を限定されたコンテキストと呼びます。有界コンテキストには、モデルを実装するコード成果物が含まれます。マイクロサービスアーキテクチャーを使用する場合、それぞれのバインドされたコンテキストは、サービスまたは場合によってはサービスのセットです。 DDDを適用し、サブドメインごとにサービスを定義することで、マイクロサービスアーキテクチャを作成できます。図2.9は、サブドメインがサービスにどのようにマップされ、それぞれが独自のドメインモデルを持つかを示しています。

DDDとマイクロサービスアーキテクチャはほぼ完全に連携しています。サブドメインと有界コンテキストのDDD概念は、マイクロサービスアーキテクチャ内のサービスにうまく対応します。また、サービスを所有する自律型チームのマイクロサービスアーキテクチャの概念は、単一のチームによって所有および開発されている各ドメインモデルのDDDの概念と完全に一致しています。さらに、このセクションの後半で説明するように、独自のドメインモデルを持つサブドメインの概念は、神のクラスを排除して分解を容易にするための優れた方法です。

アプリケーションのマイクロサービスアーキテクチャを定義するための2つの主要なパターンは、サブドメインごとの分解とビジネス能力別の分解です。しかし、オブジェクト指向設計に根を持つ分解のためのいくつかの有用なガイドラインがあります。それらを見てみましょう。

2.2.4. Decomposition guidelines

単一の責任原則
ソフトウェアアーキテクチャと設計の主な目標の1つは、各ソフトウェア要素の責任を決定することです。単一責任の原則は次のとおりです。クラスには変更する理由が1つだけあります。ロバートC.マーティンクラスが持つ責任のそれぞれは、そのクラスが変化する潜在的な理由です。クラスに独立して変更される複数の責任がある場合、そのクラスは安定しません。 SRPに従うことによって、それぞれが単一の責任を持ち、したがって変更の理由が1つあるクラスを定義します。マイクロサービスアーキテクチャを定義する際にSRPを適用し、それぞれが単一の責任を負う小規模な一貫性のあるサービスを作成することができます。これにより、サービスのサイズが縮小され、安定性が向上します。新しいFTGOアーキテクチャは、実際のSRPの例です。消費者注文の取次ぎ、注文の準備、配送に食品を払うことの各側面は、別個のサービスの責任です。

Common Closure Principle
他の有用な原則はCommon Closure Principleです。パッケージ内のクラスは、同じ種類の変更に対して一緒に閉じなければなりません。パッケージに影響する変更は、そのパッケージ内のすべてのクラスに影響します。 Robert C. Martin 2つのクラスが同じ理由のためにロックステップで変更された場合、それらは同じパッケージに属します。おそらく、たとえば、これらのクラスは特定のビジネスルールの異なる側面を実装します。目標は、ビジネスルールが変更された場合、開発者は少数のパッケージ（理想的には1つ）のコードを変更するだけでよいということです。 CCPを遵守すれば、アプリケーションの保守性が大幅に向上します。

マイクロサービスアーキテクチャを作成し、同じ理由で変更されたコンポーネントを同じサービスにパッケージングするときは、CCPを適用できます。これにより、要件の変更によって変更および展開が必要なサービスの数が最小限に抑えられます。理想的には、変更は単一のチームと単一のサービスにのみ影響します。 CCPは、分散モノリス防止パターンに対する解毒剤である。

SRPとCCPはBob Martinによって開発された11の原則のうちの2つです。これらは、マイクロサービスアーキテクチャを開発する場合に特に便利です。残りの9つの原則は、クラスとパッケージを設計するときに使用されます。 SRP、CCP、およびその他のOOD原則の詳細については、ボブ・マーティンのウェブサイト（http://butunclebob.com/ArticleS.UncleBob.PrinciplesOfOod）の記事「オブジェクト指向設計の原則」を参照してください。 SRPやCCPに加えて、ビジネス機能とサブドメインによる分解は、アプリケーションをサービスに分解するための優れた手法です。それらを適用し、マイクロサービスアーキテクチャを開発するには、トランザクション管理とプロセス間通信の問題を解決する必要があります。
-->