精読「マイクロサービスアーキテクチャ 第2版」(第Ⅰ部 基礎 - 第2章 マイクロサービスのモデル化)
マイクロサービスアーキテクチャ 第2版
マイクロサービスの設計、実装、運用に必要なベストプラクティスや最新技術を解説した、実践的なガイドブックです。これを読めば、マイクロサービスに関してそれっぽい会話もできますよ。
関連記事
MusicCorpの紹介
- マイクロサービスの概念が実世界でどのように機能するか、MusicCorp社という架空の会社を題材にして考えていく
適切なマイクロサービス境界にするには
情報隠蔽
マイクロサービスの内部の詳細を隠すことで、開発の効率化、理解のしやすさ、柔軟性を高め、システムの変更や拡張を容易にする
凝集
関連する機能や振る舞いを一緒にまとめて、変更やリリースを一箇所で行えるようにすることで、システムの変更を容易にし、複数のサービスを同時にリリースするリスクや時間を減らすことを目指す概念
結合
疎結合のサービスは独立して変更やデプロイが可能で、疎結合を目指すことでパフォーマンスや変更の影響を最小限に抑えられる
結合と凝集の相互関係
凝集度が高く結合度が低い場合、システム構造は安定し、マイクロサービスの独立したデプロイや並列作業が可能になる[1]
結合の種類
ドメイン結合
マイクロサービスが他のサービスの機能を利用するために対話し、依存関係を形成する状況
パススルー結合
あるマイクロサービスが別のサービスのデータを、さらに下流のサービスに渡す状況であり、呼び出し元が呼び出し先サービスの動作や依存関係を知っている必要があるため、問題を引き起こす可能性がある結合形式の1つ
時間的結合(サービス間で同期的に依存し合う状態)について
時間的結合とは、あるマイクロサービスが処理を完了するために、別のマイクロサービスが同時に動作し利用可能である必要がある状況であり、これによりリソース競合やスケーラビリティの問題が発生することがある。回避策の1つは、メッセージブローカーなど、何らかの非同期通信を使うこと
1. 最も良くないパススルー結合:別の下流サービスでデータを必要とするためだけに、あるマイクロサービスにデータを渡す
2. 下流サービスと直接通信:呼び出し元のマイクロサービス(注文
)が、仲介者(倉庫
)を迂回する
3. 下流サービスが必要なことを上流サービスから隠蔽する:倉庫
に必要な情報を取り込ませ、ローカルで出荷目録を構築させること
4. 中継するサービスは完全にデータを渡すだけに徹する:1と同じように注文
が依然として倉庫
を経由して配送
に出荷目録を送るが、倉庫
は出荷目録自体の構造を全く認識しないようにする(出荷目録を一塊のデータのように扱う)
共通結合
複数のマイクロサービスが同じ共有データベースを利用する状況で、データ構造の変更が複数のマイクロサービスに同時に影響を与えてしまう
1. 静的参照データの共有はさほど害はない:頻繁に変更されることはなく、読み取り専用であるため
2. 共有テーブルを読み書きして注文の状態を管理する:注文
と倉庫
の両方が、注文のライフサイクルの異なる面を管理していて、状態遷移[2]を管理する責任を共有している点が問題
状態遷移図(ステートマシン)
3. 単一のマイクロサービスが注文状態を管理する:注文
サービスが注文に関する信頼できる情報源となり、注文集約に関連する許容される状態遷移の管理を任せる
内容結合
上流サービスが下流サービスの内部に到達し、その内容状態を変更する状況のこと
過不足のないドメイン駆動設計(DDD)
マイクロサービスの境界を決めるためには、ドメイン駆動設計を使って、実際の業務内容に基づいたドメインモデルを作成することが重要
ユビキタス言語
ユーザと開発者が共通の言葉を使うことで、簡単に実世界のドメインをモデル化し、コミュニケーションを改善する考え方
アグリゲート(集約)
ドメイン駆動設計において、実際のドメイン概念(例:注文、請求書、在庫品目など)を表す重要な単位
- アグリゲートはライフサイクルを持ち、状態遷移を管理する自己完結型のユニットとして扱う
- マイクロサービスは通常、1つのアグリゲートのライフサイクルとデータを管理し、アグリゲート間の関係はサービス間で明示的に参照される(1つのマイクロサービスが複数のアグリゲートを持つこともある)
- システムの分割方法にはさまざまなアプローチがあるが、初期はユーザーのメンタルモデルに基づいて設計が進められます。
境界付けられたコンテキスト(コンテキスト境界)
組織内で特定の責務を果たす範囲を示し、その内部での実装詳細や関心事を外部から隠蔽し、必要に応じてアグリゲートやサービス間の依存関係を明確にする概念
アグリゲート、境界付けられたコンテキストのマイクロサービスへのマッピング
- 初期段階では、境界づけられたコンテキスト全体を1つのマイクロサービスとして扱うのが合理的
- マイクロサービスを小さな単位に分解する際は、アグリゲートを分解せず、1つのアグリゲードを1つのサービス内に維持するべき
- 境界付けられたコンテキストをさらに細分化しながらも、外部からは単一のサービスとして見せることができる
イベントストーミング
ドメインモデルを表面化するための協調的ブレインストーミング技法。技術者や非技術者を含む全ての利害関係者が参加し、共同作業を通じてモデルを構築することで、共通の理解を形成する。
- 基本プロセス
- 参加者の準備:モデル化対象のドメインに関わる全ての利害関係者を一堂に集める。広いスペースを用意し、壁にクラフト紙を貼って情報を視覚化。主要なツールは色分けした付箋。
- イベントの特定:システム内で関心のある事象(ドメインイベント)をオレンジ色の付箋で記録。
- コマンドの特定:イベントを引き起こすアクション(コマンド)を青色の付箋で記録。
- アグリゲートの特定:イベントやコマンドを基にアグリゲート(システム内の主要なデータ構造)を黄色の付箋で整理。
- 境界づけられたコンテキストの構築:アグリゲートを組織の構造に基づきグループ化して、全体像を明確化。
-
ポイント
- 共同作業の重要性:すべての参加者の視点を統合し、現場の知識を明らかにする。
- 立って参加する提案:全員の集中を促すため、椅子を撤去。ただし、身体的な配慮も必要。
-
柔軟性:イベントストーミングで得られたモデルは、イベント駆動型システムやリクエスト/レスポンス指向システムの実装に応用可能。
Model Event Storming Results in Context Mapperより
マイクロサービス向けのドメイン駆動開発(DDD)の例
以下のポイントから、ドメイン駆動設計はマイクロサービスの設計・運用において効果的なアプローチである
境界づけられたコンテキスト(Bounded Context)
ドメイン駆動設計は、情報隠蔽を重視し、システムの各部分を明確に分離する。これにより、他の部分に影響を与えずに変更が可能となり、安定したマイクロサービス境界の確立が容易になります。
ユビキタス言語(Ubiquitous Language)
共通の言語を用いることで、APIやイベント形式の設計がスムーズになり、各コンテキスト内での柔軟な変更が可能になる。
変更の容易さ
ビジネスの要求に応じた変更が、単一のマイクロサービス境界内で完結する可能性が高いため、迅速なデプロイが可能になる。
ビジネス中心のソフトウェア設計
ドメイン駆動設計はビジネスドメインを中心に据えることで、開発者のドメイン知識を向上させ、技術とビジネスの橋渡し役を果たす。これにより、ユーザのニーズに対する理解が深まり、製品開発やデリバリの質が向上する。
組織構造への適合
ドメイン駆動設計は、技術アーキテクチャを組織構造に合わせるための手段として機能する。ITとビジネスの間に存在するサイロを分解し、より協調的な環境を促進する。
ビジネスドメイン境界の代替手段
ドメイン駆動設計はマイクロサービスアーキテクチャを構築する際に非常に有効だが、マイクロサービス境界を見つける唯一のテクニックではない。境界を見つける際に考慮する他の要因を、いくつか見ていく。
変動性
変動性を基にしたシステムの分解方法には賛否があり、特にマイクロサービス移行の際には一概に推奨すべきではないという意見もある。変動性を基にした分解は、頻繁に変更が必要な部分を抽出し効率的な作業を目指すが、スケーリングが主な課題である場合には効果が薄い
データ
データの性質によってシステムの分解方法を選ぶことがある
技術
技術の選択がシステムの境界を決定する重要な要素となり、異なるランタイムモデルを混在させる場合や、特定の言語(例:Rust) で機能を実装する必要がある場合、分解方法が大きく影響を受ける[5]
組織
組織構造を考慮せずにシステムアーキテクチャを設計すると、後々問題が発生する可能性があるため、組織の状況に応じたアーキテクチャ設計が求められる
混合モデルと例外
マイクロサービスの境界を定義する際は、ドメイン駆動設計を基にしつつ、状況や要求に応じて柔軟にアプローチを調整する必要がある。[6]いずれにしても、情報隠蔽と結合、凝集の相互関係を理解すれば、マイクロサービス境界の定義で最悪の結果は避けられる。
参考
-
凝集はモジュール内部の関連性、結合はモジュール間の関連性を指し、最適なバランスを取ることが重要 ↩︎
-
ステートマシンとも呼ぶ ↩︎
-
例えば、経理部門では「資産」と呼ばれ、倉庫ではそのまま「在庫品目」と呼ばれるかもしれない。 ↩︎
-
倉庫内の「在庫品目」の情報は、棚の位置などの詳細は経理には公開しませんが、在庫品目の総数など、重要な情報だけを経理と共有する。 ↩︎
-
るマイクロサービスが、パフォーマンス向上のためにRustで実装された一部の機能を持っているとします。この場合、Rustのランタイムと他のサービスで使用している例えばJavaやNode.jsのランタイムが異なるため、異なるランタイム間での統合や通信が問題となり、システムの境界をどのように定義するかが重要になる ↩︎
-
例えば、ビジネスドメインや技術的な要因(異なる言語を使った実装など)によって、マイクロサービスの境界を変更する必要が生じることがある。これにより、組織的やドメイン駆動のサービス境界を設計する際、柔軟な対応が求められる。 ↩︎
Discussion