サービスベースアーキテクチャとは？

## はじめに

:::note info
この記事は、[リンクアンドモチベーションアドベントカレンダー2024](https://qiita.com/advent-calendar/2024/lmi)の記事です。
:::

開発をしていると
「システムが大きくなりすぎて、変更が怖い」、「サービスごとの開発スピードに差が出てきた」
という課題に直面することがあります。

そんな時の選択肢の一つとしてサービスベースアーキテクチャ（Service-Based Architecture, SBA）が選択肢の１つとして考えられます。
本書ではサービスベースアーキテクチャのポイントをまとめていきます。


## サービスベースアーキテクチャの基礎

### なぜサービスベースアーキテクチャが必要なのか？

#### モノリシックアーキテクチャの課題

モノリシックアーキテクチャでは、以下のような問題に直面することがあります。

- **変更が困難:** 小さな変更でも全体のデプロイが必要
- **スケーラビリティの制約:** 特定の一部の機能のみをスケールすることが難しい
- **開発効率の低下:** チーム間でコードが干渉しやすく、並行開発が困難


```mermaid
graph TD
  subgraph モノリシックアーキテクチャ
      A[商品管理] -->|商品データ保存| DB[(データベース)]
      B[注文管理] -->|注文データ保存| DB
      C[ユーザー管理] -->|ユーザーデータ保存| DB
      D[在庫管理] -->|在庫データ保存| DB
  end
```

#### マイクロサービスアーキテクチャの課題

一方で、マイクロサービスアーキテクチャ（MSA）も万能ではありません。

- **過剰な複雑性:** サービス粒度が細かすぎると、管理と運用のコストが急増
- **デバッグが困難:** 複数のサービスにまたがる障害を追跡するのは難しい
- **高い技術要件:** Kubernetesやメッセージキューなど、高度なツールの導入と運用が必須
- **通信オーバーヘッド:** 非同期通信が一般的であるため、パフォーマンスが低下する場合がある

```mermaid
graph TD
  subgraph "商品管理サービス"
    PRO["API: /products/{id}"]
    PRO_DB[("商品管理データベース")]
  end
  
  subgraph "在庫管理サービス"
    IN["API: /inventory/{id}"]
    IN_DB[("在庫管理データベース")]
  end
  
  subgraph "注文管理サービス"
    ORD["API: /orders/{id}"]
    ORD_DB[("注文管理データベース")]
  end

  subgraph "ユーザー管理サービス"
    USR["API: /users/{id}"]
    USR_DB[("ユーザ管理データベース")]
  end

  PRO --> PRO_DB
  IN --> IN_DB
  ORD --> ORD_DB
  USR --> USR_DB

  注文管理サービス --> |ユーザ情報取得|ユーザー管理サービス
  注文管理サービス --> |商品情報取得|商品管理サービス
  商品管理サービス --> |在庫情報取得|在庫管理サービス
```

#### サービスベースアーキテクチャの選択

サービスベースアーキテクチャは、モノリシックとマイクロサービスの中間的なアプローチとして注目されています。
サービスアーキテクチャは、システムを複数の機能単位（サービス）に分割する設計手法を指します。このアーキテクチャは、モノリシックアーキテクチャとは異なり、システム全体を1つの大きなコードベースとして管理するのではなく、サービスごとに独立して開発・デプロイできる構造を持っています。


| 特徴                 | モノリシックアーキテクチャ             | サービスベースアーキテクチャ          | マイクロサービスアーキテクチャ       |
|--------------------------|------------------------------------|------------------------------------|------------------------------------|
| **サービス粒度**     | システム全体を1つに統合             | 中規模の機能単位                 | 極小の単一責任単位              |
| **スケーラビリティ**   | 全体を一括でスケール               | 部分的にスケール可能              | 完全に独立してスケール可能        |
| **通信方式**         | 内部メソッド呼び出し              | 同期通信（REST、gRPCなど）が中心   | 非同期通信（メッセージキュー）が中心  |
| **初期導入コスト**     | 低い                         | 中程度                        | 高い                         |
| **デプロイ**         | 単一のデプロイメントで管理          | サービス単位で個別にデプロイ可能      | 完全に独立してデプロイ可能         |
| **データ管理**       | 単一のデータベースを使用            | 共有データベースまたは部分的な分離    | 各サービスが独立したデータベースを持つ |
| **運用コスト**       | 低い                         | 中程度                        | 高い                         |
| **適用規模**         | 小〜中規模のシステム              | 中規模〜大規模のシステム           | 大規模なシステム                |


```mermaid
graph TD
  subgraph サービスベースアーキテクチャ
    subgraph 共有データベース
      UserDB[(ユーザーデータ)]
      OrderDB[(注文データ)]
      ProductDB[(商品データ)]
      InventoryDB[(商品データベース)]
    end
    UserService["ユーザーサービス<br>API: GET /users/{id}"]
    OrderService["注文サービス<br>API: GET /orders/{id}"]
    ProductService["商品サービス<br>API: GET /products/{id}"]
    InventoryService["在庫サービス<br>API: /inventory/{id}"]
    
    UserService --> 共有データベース
    OrderService --> 共有データベース
    ProductService --> 共有データベース
    InventoryService --> 共有データベース


    OrderService --> |ユーザ情報取得|UserService
    OrderService --> |商品情報取得|ProductService
    ProductService --> |在庫情報取得|InventoryService
  end
```

## モノリスからサービスベースアーキテクチャへの移行

### ステップ1: サービスの分割

モノリスを分割し、各機能を独立したサービスに分ける。
（必ずしもすべてを独立サービスにする必要はありません。初期段階では疎結合を意識しながら、主要な機能を分割することが重要です。）

```mermaid
graph TD
  商品管理 -->|商品データ保存| DB[(データベース)]
  注文管理 -->|注文データ保存| DB
  ユーザー管理 -->|ユーザーデータ保存| DB
```

これをサービスごとに分割します。


```mermaid
graph TD
  ProductService["商品サービス<br>API: /products"] -->|商品データ共有| DB[(データベース)]
  OrderService["注文サービス<br>API: /orders"] -->|注文データ共有| DB
  UserService["ユーザーサービス<br>API: /users"] -->|ユーザーデータ共有| DB
```

### ステップ2:  APIゲートウェイの導入

Amazon API Gatewayなどのゲートウェイサービスを使って通信を統一管理します。

- クライアントが複数のサービスに直接アクセスすることを防止
- 各サービスへのリクエストをルーティング
- 認証やレート制限などの機能を統一的に提供


```mermaid
graph TD
  Client[クライアント] --> Gateway["APIゲートウェイ<br>GET /api/orders"]
  Gateway --> OrderService["注文サービス<br>API: /orders"]
  Gateway --> ProductService["商品サービス<br>API: /products"]
```

## データベースの分離

サービスベースアーキテクチャのデータベースは、単一のトランザクションでの管理、クエリの結合の活用の観点から基本的に１つだけを管理するようになっています。

しかし、サービスごとのデータを他のドメインサービスで参照されない場合、独自のデータベースとして管理することも考えられます。

### 1つだけのデータベースを管理する場合
```mermaid
graph TD
  SharedDB["共有データベース"]
  ProductService["商品サービス"] -->|商品テーブル操作| SharedDB
  OrderService["注文サービス"] -->|注文テーブル操作| SharedDB
  UserService["ユーザーサービス"] -->|ユーザーテーブル操作| SharedDB
```

### 他のドメインサービスから参照されない独自のデータベースとして管理する場合

```mermaid
graph TD
  SharedDB["共有データベース"]
  UserDB["ユーザーデータベース"]
  ProductService["商品サービス"] -->|商品テーブルへの書き込み| SharedDB
  OrderService["注文サービス"] -->|注文テーブルへの操作| SharedDB
  UserService["ユーザーサービス"] -->|ユーザーテーブルへの操作| UserDB
```

## さいごに

サービスベースアーキテクチャはモノリシックアーキテクチャとマイクロサービスアーキテクチャとの中間的な構成が採用されています。

それぞれのアーキテクチャにはトレードオフがあるため、適切な選択ができるよう、特徴を抑えることが重要なのでしっかり押さえておきたいと思います。

## 参考

https://www.oreilly.co.jp//books/9784873119823/

https://www.oreilly.co.jp/books/9784814400010/