「モノリスからマイクロサービスへ」を読んでマイクロサービスに入門してみた

## はじめに
[レバウェル開発部アドベントカレンダー](https://qiita.com/advent-calendar/2024/levwell)10日目です。
はじめまして。レバウェル開発部の佐藤です。
私たちのチームでマイクロサービスの導入を検討する機会があり、その過程で「[モノリスからマイクロサービスへ](https://www.oreilly.co.jp/books/9784873119311/)」という書籍を手に取りました。この本を通じて得た知見を整理し、自分なりの解釈を交えたメモを公開します。

## 1章 必要十分なマイクロサービス
### マイクロサービスの定義
マイクロサービスとは、ビジネスドメインをモデル化した、小規模かつ独立してデプロイ可能なサービスを指します。本書では以下のような特徴を強調しています。
1. 独立デプロイ可能性
他のサービスに影響を与えずに変更・デプロイが可能であること。これは、システムの変更の柔軟性を大きく高める重要な性質です。
    - 例: ECサイトで、カートサービスを修正する際、商品検索や決済機能に影響を与えない。
    - 具体的には、APIを通じて通信するなど、疎結合な構造を持つことが前提になります。
2. 技術への依存が低い
マイクロサービスは基本的に使用する技術に依存しません。異なる言語やフレームワークを用いても、ネットワーク通信ができれば成り立ちます。
    - 例: 一部サービスはPython、他はGoで実装することも可能。
3. サイズは文脈依存
サイズそのものは重要ではなく、チームの能力やシステムの目的に応じて調整すべきだと述べられています。大きすぎるサービスを「モノリス」と呼ぶべきではなく、逆に小さすぎるものも管理のコストがかかります。

### 3層アーキテクチャの限界とマイクロサービスの台頭
3層アーキテクチャ（プレゼンテーション、アプリケーション、データベース層の分離）は、かつて主流でした。このアーキテクチャの採用背景には、以下のような合理性がありました。
- **各層に特化したチーム編成**
    - フロントエンドチーム（プレゼンテーション層）
    - ビジネスロジック担当チーム（アプリケーション層）
    - データベースチーム

各層の専門性を活かしやすい分業体制を築けた点が魅力でした。
しかし、現代のビジネス環境ではこのモデルが課題を抱えています。
- **ビジネス要件の変更に弱い**
例えば、新機能を追加する際、複数の層をまたいで変更が必要になるため、調整コストが高まります。
- **技術的な凝集度の偏り**
各層が技術的にはまとまっている一方で、ビジネス機能が散らばっており、変更が難しくなる傾向があります。

### データ所有の考え方
マイクロサービスの設計において、データベースの分割は非常に重要です。本書では、以下の原則を提示しています。
- **データベースの共有を避ける**
必要最低限のケースを除き、サービス間で同じデータベースを共有してはいけません。共有データベースは、実装結合や依存関係の温床となるからです。
- **何を共有し、何を隠すかを明確にする**
各サービスが自分のデータを完全に所有し、必要に応じて他サービスと連携する形が理想です。

例えば、ECサイトでの「注文管理サービス」は、商品情報を参照する必要があります。この場合、商品情報の全てを公開するのではなく、APIを通じて必要な部分のみを提供する設計が推奨されます。

### マイクロサービスの利点と課題
**利点**
- **理解しやすさ**
各サービスが小さいため、担当者がその機能全体を把握しやすくなります。
- **並行開発のしやすさ**
サービスごとに独立して開発・デプロイできるため、大規模なチームでも効率的に作業できます。
- **技術選択の自由度**
必要に応じて最適な技術スタックを選べる点も大きな利点です。

**課題**
- **ネットワークの脆弱性**
ネットワーク通信が失敗するリスクが常に伴います。このため、再試行やフォールバックの仕組みを設ける必要があります。
- **分散トランザクションの管理**
トランザクションがサービスをまたぐ場合、一貫性の管理が非常に複雑になります（サーガパターンなどが必要）。

### マイクロサービス化の重要な問い
本書では、以下の質問を自問することがマイクロサービス化の成功の鍵だと述べています。
1. **自分たちはどれだけの数のサービスを扱えるのか？**
人員や運用体制により、扱えるサービスの数には限界があります。最適な数を見極める必要があります。
2. **マイクロサービスの境界線はどのように定義すべきか？**
境界線の定義には、ドメイン駆動設計（DDD）の概念を用いることが有効です。「共に変更されるコード」をまとめ、他サービスと疎結合にする設計を目指します。

### 学び
1章を読んで、マイクロサービスの利点を活かすためには、単なる「分割」ではなく、戦略的な設計が重要であると感じました。特に「デプロイの単位」としての独立性を確保することが、本質的なポイントだと思います。また、データ所有の考え方やドメイン駆動設計の重要性も、実際のシステム設計で大いに参考になる内容でした。

## 2章 移行を計画する
### マイクロサービス移行の目的を理解する
本章の冒頭で強調されるのは、「マイクロサービスへの移行そのものが目的ではない」という点です。移行はあくまで手段であり、最終的な目標を達成するための一部にすぎません。

**移行の理由と成果**
移行を成功させるためには、明確な理由と期待される成果が必要です。本書ではいくつかの移行理由が挙げられています。
1. **チームの自律性を高める**
    - 自律性を持つチームは、独立したサービスの運用や変更が容易になります。ただし、これはモジュラーモノリスでも達成可能であり、必ずしもマイクロサービスが唯一の解決策ではありません。
2. **市場投入までの時間を短縮する**
    - ボトルネックを特定し、その解消にマイクロサービスが役立つことがあります。たとえば、並行して複数のサービスを開発・リリースできる点が有効です。
3. **スケーラビリティの向上**
    - 負荷に応じて特定のサービスをスケールアウト（水平分散）することが可能になります。ただし、スケールアップ（垂直分散）とのコストや運用負担のトレードオフを理解する必要があります。
4. **堅牢性の向上**
    - 一部のサービスが障害を起こしても、他のサービスへの影響を最小限に抑えられます。ただし、分散化が新たな障害ポイントを増やす可能性もあり、設計の工夫が求められます。

### マイクロサービス化が適さないケース
本書では、「全てのシステムがマイクロサービスに適しているわけではない」ことが強調されています。以下のようなケースでは慎重な判断が必要です。
1. **不明瞭なドメイン**
ドメインが明確に理解されていない状態で分解を進めると、結合が多く、頻繁な変更が必要になる可能性があります。
解決策: ドメイン駆動設計（DDD）を導入し、まずはモノリスの中でドメインを明確化する。
2. **スタートアップフェーズ**
事業の方向性が固まらない段階では、迅速な開発が重要であり、モノリスの方が効率的な場合が多いです。サービスが成長し、スケーラビリティや変更の柔軟性が求められる段階でマイクロサービスを検討します。
3. **顧客の環境にインストールされるソフトウェア**
分散システムを顧客が管理する場合、その運用負担が増え、結果として顧客満足度の低下につながる可能性があります。

### 段階的移行の重要性
本書では、移行を一度に進める「ビッグバンリライト」のリスクが指摘されており、段階的な移行が推奨されています。
- **ストラングラーアプリケーション**
古いモノリスと新しいマイクロサービスを共存させながら徐々に移行する方法です。モノリス内の特定の機能を新しいサービスとして切り出し、その部分だけを新しいシステムで動作させます。
    - メリット: リスクが低く、変更の影響範囲を制限できる。
    - 注意点: 切り出した機能がモノリスのデータベースに依存している場合、さらなる設計が必要になります（詳細は4章で触れられます）。

### 移行のためのトレードオフ
移行に際して、複数の目標間でトレードオフを検討することが重要です。
たとえば、以下のような考え方が役立ちます。

| 目標           | 優先度を上げた場合の影響          |
|-----------------|-----------------------------------|
| 自律性を高める   | チーム間の一貫性が低下する       |
| コストを削減する | 開発速度が低下する               |
| 障害時の影響を抑える | システムの複雑性が増す       |


### 組織変革のプロセス
移行の成功には、技術的な実装だけでなく、組織全体の変革が必要です。本書では以下のようなプロセスを提案しています。
1. **危機意識を高める**
変革の必要性を関係者に理解してもらう。
2. **ビジョンと戦略を策定する**
移行の目標を共有し、具体的な戦略を策定。
3. **短期的な成果を実現する**
小さな成功体験を積み重ねて、関係者の支持を得る。
4. **新しい方法を文化に定着させる**
一度の移行で終わるのではなく、継続的に改善を続ける文化を醸成。

### 学び
2章を通じて、マイクロサービスへの移行を成功させるためには、明確な目標設定と段階的なアプローチが不可欠であると理解しました。また、移行を技術的な課題としてだけでなく、組織や文化の課題としても捉える視点が印象的でした。自分のプロジェクトに適用する際には、まず現状のボトルネックを明確にし、それに応じた段階的な移行計画を立てることが重要だと感じます。



## 3章 モノリスを分割する
### モノリスを変更すべきかどうか
本章の冒頭では、「既存のモノリスをどのように扱うべきか」を問いかけています。選択肢としては以下の2つが挙げられます。
1. **モノリスをそのまま維持する**
既存のコードベースが整理され、問題が少ない場合は、モノリスを維持するのも選択肢の一つです。特に、変更頻度が低い安定したシステムでは、無理に分割を進める必要はありません。
2. **段階的に分割して移行する**
逆に、頻繁な変更が必要で、チーム間の衝突や運用の非効率が目立つ場合は、モノリスを分割していくべきです。この際、一気にリライトする「ビッグバンリライト」は避け、段階的な移行が推奨されています。

### 段階的な移行アプローチ
段階的にモノリスを分割する際には、以下のアプローチが効果的です。
1. **接合部の修正から始める**
既存の振る舞いを変えず、コードの接合部を改善する。例えば、以下のような修正が挙げられます。
    - コードのモジュール化（Go言語ならパッケージ化など）
    - 明確なインターフェースの導入
2. **モジュラーモノリスを目指す**
機能ごとにコードを分離し、独立したモジュールとして扱う。この段階では、まだ1つのデプロイ単位にまとまっていますが、内部的には各モジュールが疎結合化されている状態です。
3. **マイクロサービスへの移行**
モジュール単位での分割が進んだ後、特定の機能を切り出して独立したマイクロサービスとして動かします。

### 移行パターン集
本章では、モノリスをマイクロサービスへ移行する際に活用できる具体的なパターンが紹介されています。以下はその代表例です。

#### パターン1: ストラングラーアプリケーション
**概要**
古いシステムと新しいシステムを共存させながら、徐々にモノリスを置き換えていく方法です。
- 名前の由来: ストラングラー（絞め殺し）イチジクが、既存の木に巻き付きながら成長する様子に例えられています。

**手順**
1. モノリス内から移行する部分を決める（例: 注文管理機能）。
2. 新しいマイクロサービスとして移行部分を実装する。
3. 新しいマイクロサービスをデプロイし、移行した機能への呼び出しをリダイレクトする。
4. 古い機能を安全に削除する。

**メリット**
- 段階的に移行が進められるため、リスクが低い。
- 新旧システムを同時に運用できるため、問題発生時のロールバックが容易。

**注意点**
- モノリスのデータベースを共有する場合、整合性の問題が発生しやすい。

#### パターン2: UI合成
**概要**
サーバー側の処理だけでなく、フロントエンド（UI）も分割するアプローチです。
**手法**
- **ページ合成**: ページ単位で新しいマイクロサービスを構成する。CDNなどを活用して移行。
- **ウィジェット合成**: ウィジェット単位でUIを分割し、それぞれ独立したサービスとして開発。
- **マイクロフロントエンド**: UIコンポーネントを異なる技術で構築し、それらを組み合わせて運用する。

**メリット**
- フロントエンドの変更がバックエンドに影響を与えにくい。
- UIチームの自律性が高まる。

**課題**
- フロントエンド側の統合が複雑になる可能性がある。

#### パターン3: 抽象化によるブランチ
**概要**
マイクロサービス化したい機能がモノリスの深い部分に存在する場合、直接移行するのではなく、抽象化を導入してブランチ作業を行います。
**手順**
1. **抽象を作成**: モノリス内にインターフェースを定義し、既存機能を隠蔽。
2. **新実装の作成**: 抽象化したインターフェースに新しい実装を追加。
3. **切り替え**: 検証後、新しい実装を有効化。

**メリット**
- 既存コードに直接手を加えるリスクを軽減。
- 古い実装との比較が容易。

#### パターン4: 同時実行
**概要**
新旧の実装を同時に実行し、結果を比較して移行の安全性を検証する方法です。
**活用シーン**
- 重要な機能の移行時に適用。
- 新しいサービスが信頼できるかどうかを確実に検証したい場合。

**課題**
- 両方の実装を維持するための運用コストが高い。

### 学び
本章で最も印象的だったのは、「モノリスを分割する際の慎重な計画と段階的な実行」の重要性です。特に「ストラングラーアプリケーション」のような段階的な移行手法は、実務でも取り入れやすいと感じました。また、移行の際にモジュール化や抽象化を意識することで、移行後の柔軟性も確保できることが分かりました。

## 4章 データベースを分割する
### データベース分割の必要性
モノリスでは、1つの大きなデータベースがシステム全体を支えています。しかし、マイクロサービスでは各サービスが独自のデータを持つことが原則です。本章では、以下の理由からデータベース分割の重要性が強調されています。
1. **独立性の確保**
    - 各サービスが独自のデータを持つことで、他のサービスに影響を与えずに変更やスケーリングが可能になります。
2. **実装結合の回避**
    - 複数のサービスが同じデータベースを共有すると、スキーマ変更や整合性管理が複雑化し、進化が阻害されます。
3. **ビジネスロジックの凝集度向上**
    - データとロジックが同じ境界内にあることで、変更や拡張が容易になります。

### 共有データベースの問題点
本書では、単一のデータベースを共有する場合に生じる具体的な課題が指摘されています。
- **スキーマの管理が困難**
どの部分が安全に変更可能なのかを把握するのが難しくなります。例えば、あるサービスがテーブルを変更すると、別のサービスが予期せぬエラーを起こす可能性があります。
- **性能の低下**
一度に複数のサービスが同じデータベースにアクセスすることで、スケーラビリティや応答時間が悪化する可能性があります。
- **トランザクション管理の複雑化**
共有データベースでは、複数のサービスがトランザクションを管理しなければならず、ACID特性の維持が難しくなります。

### データベース分割のパターン
本章では、データベース分割を進めるための具体的なパターンがいくつか紹介されています。

#### パターン1: データベースビュー
**概要**
データベースの特定部分をビューとして公開し、直接のアクセスを制限する方法です。
- 例: 必要なカラムだけを公開するビューを作成し、他サービスはそのビューを介してデータを参照する。

**メリット**
- 既存スキーマを変更せずにデータ分離を実現できる。
- 他サービスから参照されるデータの粒度を制御可能。

**課題**
- ビューは基本的に読み取り専用であるため、書き込みを伴う要件には対応できません。
- Viewが複雑になると管理が煩雑になる可能性があります。

#### パターン2: データベースをラップするサービス
**概要**
データベースへの直接アクセスを排除し、専用のAPIやサービスを介してデータにアクセスする手法です。
**メリット**
- データベースへの依存が軽減され、サービス間のインターフェースが明確化される。
- 書き込みや読み取りに関する権限を細かく制御可能。

**課題**
- データベースラッパー自体がボトルネックになる可能性がある。
- APIの設計や実装に追加コストが発生する。

#### パターン3: サービスのインターフェースとしてのデータベース (DaaS)
**概要**
データベース自体をサービスとして公開し、他サービスがこのデータベースを使用する方法です。
**メリット**
- 複雑なデータクエリ（特にJOIN）が必要な場合に有効。
- データベースを複数のサービスで共有する際の柔軟性が高い。

**課題**
- データがstaleになる可能性がある。
- データベースの管理が中央集権化し、運用負荷が増加する。

### 所有権とデータの同期
データベース分割を進める際、データの所有権と同期が重要なテーマとなります。
- **データの所有権を明確化する**
各サービスが自分のデータを完全に所有し、他サービスがそのデータにアクセスする際にはAPIなどを利用する。
- **データ同期の課題**
分散データベースではデータの同期に課題が生じます。同期方法として以下のアプローチが考えられます。
    - **トランザクションログ監視**: データベースのトランザクションログを監視し、変更を他サービスに伝播させる。
    - **バッチ処理**: 定期的にデータをスキャンし、変更内容を同期する。

### トランザクション管理の課題
モノリスでは単一データベース内でACIDトランザクションが保証されますが、マイクロサービスでは以下の問題が発生します。
1. **分散トランザクションの難しさ**
複数のサービス間で一貫性を保つには、2フェーズコミットや分散トランザクションが必要になりますが、これらは複雑で性能面のリスクがあります。
2. **サーガパターンの活用**
分散トランザクションの代替として、サーガパターン（連続的なローカルトランザクションのチェーン）を用いる方法が紹介されています。
    - **オーケストレーション型サーガ**: 中央制御でトランザクションを管理。
    - **コレオグラフィ型サーガ**: 各サービスがイベントを受け取り、次のステップを実行。

### 学び
この章を読んで、データベースの分割がマイクロサービス移行の成功に直結する重要な要素であることを再確認しました。特に以下の点が実践的だと感じました。
- ステップバイステップでデータの所有権を移行することが、システムの安全性と柔軟性を高める。
- トランザクション管理のために分散システム特有の設計（サーガパターンなど）を取り入れる必要性を理解する。

また、モノリスのデータベース分割を進める際には、物理的な分割よりも論理的な分割を優先するアプローチが現実的であると感じました。

## 5章 成長の痛み
マイクロサービスの採用は、サービスの成長や柔軟性をもたらす一方で、新たな課題を生みます。この章では、サービス数やチーム数が増加する中で直面しがちな問題を取り上げ、それにどう向き合うべきかの示唆が述べられています。

### サービス数の増加に伴う課題
| サービス数 | 主な課題 | 発生する兆候 |
| --- | --- | --- |
| 2～10 | 破壊的変更、レポーティング | 他サービスの機能変更による障害 |
| 10～50 | 所有権のスケール、開発者体験の低下 | 責任範囲の曖昧さ、環境構築の困難さ |
| 50～ | 全体最適と局所最適のバランス | 孤児サービスの発生、技術選択の乱立 |


### 課題への考察と対応の方向性
1. **破壊的変更のリスク**
公開されたサービスの変更は、後方互換性の欠如により他のサービスに影響を与えます。この問題に向き合うには、スキーマや契約の明確化が鍵となります。また、移行期間を設けて変更を周知する工夫も重要です。
2. **レポーティングの難しさ**
データが分散されることで、全体のデータを統合して分析することが難しくなります。専用のデータベースやデータレイクを活用する発想が有効です。
3. **所有権のスケール**
サービスが増える中で、どのチームが何を所有しているのかが曖昧になると、分散モノリス化のリスクが高まります。所有権を明確化し、組織内で責任範囲を定義する文化が必要です。
4. **開発者体験の低下**
サービス数が増えると、ローカル開発やテスト環境の構築が負担となります。スタブやモックの活用、リモート環境の利用がその負担を軽減します。
5. **技術選択の乱立**
チームごとに独自の技術選択を進めると、組織全体での非効率が生じます。一方で、標準化が進みすぎるとチームの自律性を損ねるため、バランスを取る仕組みが重要です。
6. **孤児サービスの発生**
放置されたサービスが誰の管理下にもない状態を防ぐには、定期的な棚卸しやアーカイブの実施が求められます。

### 学び
この章を通じて、マイクロサービスの成長には必然的に新たな課題が伴うことが明らかになりました。特に以下の点が示唆に富む内容でした。
1. **問題を予防する仕組み**
問題が顕在化する前に、成長過程で現れる兆候を見逃さない仕組みを作ることが重要です。これは技術的な監視体制だけでなく、組織的なコミュニケーションの場を設けることにもつながります。
2. **標準化と柔軟性の調和**
標準化による効率化と、チームの自律性を尊重する柔軟性は、しばしば対立する要素です。両者のバランスを取るための技術横断的な組織設計が鍵となります。
3. **所有権の透明性**
サービスの所有権を曖昧にすると、結果として全体の効率が低下します。責任を明確化し、それを維持するプロセスが必要です。
4. **成長の痛みを受け入れる心構え**
マイクロサービスの成長には課題が伴いますが、それらは組織の成熟と共に解決されるべきものです。痛みを乗り越えることで得られる価値を見据え、粘り強く取り組むことが大切だと感じました。

## まとめ
本書を読んで感じたのは、マイクロサービス移行は単なる技術の話ではなく、システム設計や運用、さらには組織文化そのものを見直すプロセスだということです。ただ形だけを真似るのではなく、自分たちの現状や課題をしっかりと理解したうえで、合理的な意思決定を重ねていく姿勢が何より大切だと気づきました。
最後まで読んでいただき、ありがとうございました。