マイクロサービスアーキテクチャについて勉強していると、
- オーケストレーション(Orchestration)
- コレオグラフィ(Choreography)
という言葉が出てくる。
最初に疑問に思ったのが、
処理ごとにメソッドを分けて、メインとなる処理から順番に呼び出せばいいのでは?
ということだった。
実はこの発想は、オーケストレーションの考え方にかなり近い。
ただし、「メソッドをどう分けるか」と「マイクロサービス同士をどう連携させるか」では、考えているレベルが違う。
この記事では、この違いから整理する。
まず、マイクロサービスとは?
マイクロサービスアーキテクチャ(Microservices Architecture)**とは、システムを複数の小さなサービスに分割し、それぞれを独立性の高い単位として開発・運用する考え方である。
例えばECサイトなら、
ECサイト
├── 注文サービス
├── 在庫サービス
├── 決済サービス
└── 配送サービス
のように分けられる。
ここで問題になる。
注文を受け付けたら、
注文
↓
在庫確保
↓
決済
↓
配送
という一連の処理を行いたい。
では、誰がこの順番を管理するのか?
この問題に対する代表的な考え方が、
オーケストレーション
vs
コレオグラフィ
である。
オーケストレーションとは?
**オーケストレーション(Orchestration)**とは、中心となる処理が存在し、他のサービスに「次はこれをしてください」と指示する方式である。
「オーケストラの指揮者」をイメージすると分かりやすい。
Orchestrator
│
① 在庫確保して
↓
在庫サービス
│
② 決済して
↓
決済サービス
│
③ 配送して
↓
配送サービス
Orchestrator(オーケストレーター)が一連の業務フローを管理している。
「メソッドに分けてmainから呼ぶ」のと似ている?
考え方としては似ている。
例えばJavaで、
public void processOrder() {
reserveStock();
payment();
createShipment();
}
private void reserveStock() {
// 在庫確保
}
private void payment() {
// 決済
}
private void createShipment() {
// 配送登録
}
と書いたとする。
processOrder() が全体の処理順序を知っている。
processOrder()
│
├── reserveStock()
├── payment()
└── createShipment()
これは、
中央の処理が、個々の処理を順番に呼び出す
という意味ではオーケストレーションと似ている。
ただし、これは基本的には1つのアプリケーション内部におけるメソッドの責務分割である。
マイクロサービスで考えると、
注文サービス / Orchestrator
│
├──→ 在庫サービス
│
├──→ 決済サービス
│
└──→ 配送サービス
のように、独立したサービス同士の連携になる。
つまり、
メソッドの呼び出し
↓
より大きな単位にすると
↓
サービス間の呼び出し
というイメージで考えると理解しやすい。
じゃあ、オーケストレーションでいいのでは?
ここで、
だったら全部オーケストレーションでいいのでは?
という疑問が出てくる。
実際、オーケストレーション自体は悪い設計ではない。
むしろ、
注文
↓
在庫
↓
決済
↓
配送
のように、処理順序が重要な業務フローを表現するのが得意である。
例えば、
在庫確保成功
↓
決済実行
↓
決済成功
↓
配送登録
という流れなら、中央で管理した方が理解しやすい。
オーケストレーションのメリット
1. 処理の流れが分かりやすい
Orchestratorを見ることで、
① 在庫
② 決済
③ 配送
という全体の流れを把握しやすい。
そのため、仕様の理解やデバッグもしやすい。
2. 処理順序を制御しやすい
例えば、
在庫確保に成功した場合のみ決済
決済成功した場合のみ配送
といった制御を書きやすい。
3. エラー処理をまとめやすい
例えば、
在庫確保
↓ 成功
決済
↓ 失敗
在庫確保をキャンセル
のような処理も、中央で業務フローを管理していれば理解しやすい。
オーケストレーションのデメリット
問題になるのは、Orchestratorが他サービスについて知りすぎる場合である。
例えば、
注文サービス
「在庫サービスではAを実行して……」
「この状態だったらBを実行して……」
「決済サービスではCを実行して……」
「失敗したらDを実行して……」
「配送サービスではEを……」
となっていく。
こうなると、Orchestratorが巨大化する。
さらに、
決済サービスを変更
↓
Orchestratorも変更
在庫サービスを変更
↓
Orchestratorも変更
という状態になる可能性がある。
つまり、サービスを分割したのに、中央のサービスとの結合が強くなってしまう。
「コンシューマに内部の詳細を公開しない」ともつながる
これはカプセル化の話とも関係している。
例えば注文サービスが決済サービスを利用するとき、
注文サービス
↓
「決済してください」
↓
決済サービス
くらいの契約で済むなら、注文サービスは決済サービス内部の仕組みを知らなくていい。
一方、
注文サービス
「まず決済サービスのAを実行して」
「次にBを確認して」
「この値ならCを実行して」
となると、利用する側が内部事情を知りすぎている。
すると、決済サービス内部を変更しただけなのに注文サービスまで変更する必要が出てくる。
これは疎結合を保ちにくい状態である。
そこでコレオグラフィ
コレオグラフィ(Choreography)では、中央の司令塔を置かない。
各サービスがイベントを受け取り、自分が必要な処理を実行する。
例えば、
注文サービス
│
└──「注文作成」イベント
↓
在庫サービス
│
└──「在庫確保」イベント
↓
決済サービス
│
└──「決済完了」イベント
↓
配送サービス
となる。
誰かが、
次は決済サービスを呼んでください。
と命令しているわけではない。
決済サービス自身が、
「在庫確保イベントが発生したら、自分は決済処理をする」
と判断している。
なぜ「コレオグラフィ」という名前なのか?
Choreographyは「振り付け」という意味である。
ダンスでは、中央の指揮者がリアルタイムで、
あなた右!
次あなた左!
次ジャンプ!
と命令しなくても、それぞれが振り付けに従って動く。
同じように、
サービスA
「イベントが来たから自分の処理をする」
サービスB
「このイベントが来たから自分の処理をする」
サービスC
「次は自分の仕事だ」
と各サービスが動く。
コレオグラフィのメリット
1. サービスを疎結合にしやすい
注文サービスは、
注文が作成された
というイベントを発行するだけでいい。
そのイベントを誰が利用するのかまで知らなくてもよい。
例えば、
「決済完了」イベント
│
├──→ 配送サービス
├──→ メールサービス
├──→ ポイントサービス
└──→ 分析サービス
と増やすこともできる。
イベント発行側を変更せず、新しいサービスを追加できる可能性がある。
2. 各サービスの独立性を高めやすい
それぞれのサービスが、
このイベントが来たら、この仕事をする
という責務を持つ。
中央のOrchestratorにすべての処理を集中させる必要がない。
3. イベント駆動アーキテクチャと相性がよい
例えばKafkaのようなメッセージング基盤を使い、
Producer
↓
Event
↓
Kafka
↓
Consumer
という形でサービスを連携できる。
そのため、
Kafka → イベント駆動 → コレオグラフィ
という概念はつながっている。
コレオグラフィのデメリット
1. 全体の処理が見えにくい
これは大きな問題になる。
注文
↓
イベントA
↓
在庫
↓
イベントB
├──→ 決済
├──→ メール
└──→ 分析
↓
イベントC
↓
...
サービスが増えるほど、
この注文は今どこにいるの?
が分かりにくくなる。
2. デバッグが難しくなる
オーケストレーションなら、
Orchestrator
を中心に調査できる。
コレオグラフィでは、
注文サービスのログ
↓
Kafka
↓
在庫サービスのログ
↓
Kafka
↓
決済サービスのログ
のように複数サービスをまたいで調査する可能性がある。
そのため、分散トレーシングやObservability(可観測性)が重要になる。
3. 業務フローがコード上の1か所に存在しない
オーケストレーションなら、
reserveStock();
payment();
createShipment();
を見ることで流れを理解できる。
コレオグラフィでは、その「main」に相当する場所がない。
複数サービスを確認しないと全体像が分からない場合がある。
オーケストレーションとコレオグラフィの比較
| 観点 | オーケストレーション | コレオグラフィ |
|---|---|---|
| 基本思想 | 司令塔が指示 | 各サービスがイベントに反応 |
| 中央管理 | あり | 基本的になし |
| 処理順序 | 管理しやすい | 複雑になる場合がある |
| 全体像 | 把握しやすい | 見えにくくなりやすい |
| デバッグ | 比較的しやすい | 難しくなりやすい |
| 疎結合 | 設計次第 | 実現しやすい |
| サービス追加 | Orchestrator変更が必要な場合あり | イベント購読だけで追加できる場合あり |
| 中央への依存 | 高くなりやすい | 小さくしやすい |
| イベント駆動との相性 | 利用可能 | 特に良い |
マイクロサービスではオーケストレーションは良くない?
そうとは限らない。
ここは重要で、
マイクロサービス
↓
コレオグラフィにするべき
という単純な話ではない。
オーケストレーションにもコレオグラフィにもメリット・デメリットがある。
例えば、
在庫確保
↓
決済
↓
配送
のように、順序・成功・失敗をきちんと制御したい業務フローでは、オーケストレーションが分かりやすい。
一方、
「注文完了しました」
必要なサービスは
このイベントを利用してください
という用途なら、コレオグラフィが向いている。
実際には組み合わせることもできる
重要なのは、システム全体を、
全部オーケストレーション
または、
全部コレオグラフィ
に統一する必要はないということ。
例えば、
注文処理
│
│ オーケストレーション
↓
在庫 → 決済 → 注文確定
│
│ 注文完了イベント
↓
┌────────┼────────┐
↓ ↓ ↓
メール 分析 ポイント
とすることもできる。
注文成立までの重要な業務フローは中央で制御する。
一方、注文成立後の、
- メール送信
- 分析
- ポイント付与
などはイベントを購読して各サービスが動く。
このように、用途に応じて両方を使い分けることができる。
ここまで学んだ概念をつなげる
今回の話は、これまで出てきた用語ともつながっている。
マイクロサービス
│
↓
サービス同士をどう連携する?
│
├─────────────┐
↓ ↓
Orchestration Choreography
│ │
中央でフロー管理 Eventで連携
│ │
│ ↓
│ Kafka
│ │
│ ↓
└──────→ 疎結合
│
↓
処理が分散して見えにくい
│
↓
Observability
│
↓
分散トレーシング
さらにカプセル化という観点では、
サービスの内部実装を
利用者にできるだけ公開しない
↓
依存を減らす
↓
疎結合
という話にもつながる。
まとめ
オーケストレーションとは、
中央の司令塔が各サービスに処理を指示する方式
である。
Javaで、
reserveStock();
payment();
createShipment();
とメイン処理から各処理を呼び出す考え方に似ている。
一方、コレオグラフィとは、
中央の司令塔を置かず、各サービスがイベントに反応して処理する方式
である。
注文作成
↓ Event
在庫確保
↓ Event
決済
↓ Event
配送
となる。
オーケストレーションは処理の流れを理解・制御しやすいが、中央への依存が強くなりやすい。
コレオグラフィはサービスを疎結合にしやすいが、処理全体を追跡しにくくなる。
したがって、
「マイクロサービスではオーケストレーションを使ってはいけない」
ではない。
重要なのは、
中央で管理した方がよい業務フローなのか、それとも各サービスを独立して反応させた方がよいのか
を考えて選択することである。
そして今回の内容を理解すると、
マイクロサービス → カプセル化 → 疎結合 → イベント駆動 → Kafka → Observability
という、これまで個別に学んできた概念が一つの設計上の話としてつながってくる。