10日で覚えるSystem DesignDay 6: マイクロサービスとAPI設計

Day 6: マイクロサービスとAPI設計

今日学ぶこと

  • モノリスとマイクロサービスのトレードオフ
  • REST、gRPC、GraphQLの比較と使い分け
  • API Gatewayパターン
  • サービスディスカバリ
  • レートリミティングとスロットリング
  • 認証(OAuth 2.0、JWT)
  • 冪等性(Idempotency)

モノリスとマイクロサービス

モノリスアーキテクチャ

モノリスはすべての機能が1つのアプリケーションにまとまったアーキテクチャです。

flowchart TB
    subgraph Monolith["モノリスアプリケーション"]
        direction TB
        UI["UI Layer"]
        BL["Business Logic"]
        DA["Data Access"]
    end
    subgraph DB["データベース"]
        Single["単一DB"]
    end
    UI --> BL --> DA --> Single
    style Monolith fill:#3b82f6,color:#fff
    style DB fill:#8b5cf6,color:#fff

マイクロサービスアーキテクチャ

マイクロサービスは、機能ごとに独立したサービスとして分離するアーキテクチャです。

flowchart TB
    Client["クライアント"]
    GW["API Gateway"]
    Client --> GW
    subgraph Services["マイクロサービス"]
        S1["ユーザーサービス"]
        S2["注文サービス"]
        S3["決済サービス"]
        S4["通知サービス"]
    end
    GW --> S1
    GW --> S2
    GW --> S3
    GW --> S4
    subgraph Databases["データベース"]
        DB1["Users DB"]
        DB2["Orders DB"]
        DB3["Payments DB"]
        DB4["Notifications DB"]
    end
    S1 --> DB1
    S2 --> DB2
    S3 --> DB3
    S4 --> DB4
    style Services fill:#22c55e,color:#fff
    style Databases fill:#8b5cf6,color:#fff

トレードオフ比較

観点 モノリス マイクロサービス
開発速度(初期) 速い 遅い(インフラ構築が必要)
デプロイ 全体を一括デプロイ サービス単位でデプロイ
スケーリング 全体をスケール 個別にスケール
技術スタック 統一 サービスごとに選択可能
障害の影響範囲 全体に波及 影響を局所化できる
データ整合性 ACID トランザクション 結果整合性(Eventual Consistency)
運用の複雑さ 低い 高い(監視、ログ集約が必要)
チーム編成 大きなチーム 小さな独立チーム

面接のポイント: 「マイクロサービスが常に正解」ではありません。プロジェクトの規模、チームのスキル、ビジネス要件に応じて判断することが重要です。


API設計:REST vs gRPC vs GraphQL

REST(Representational State Transfer)

RESTは最も広く使われているAPI設計スタイルです。HTTPメソッドを使ってリソースを操作します。

GET    /api/users          # List users
POST   /api/users          # Create user
GET    /api/users/{id}     # Get user
PUT    /api/users/{id}     # Update user
DELETE /api/users/{id}     # Delete user

RESTのベストプラクティス:

  • 名詞でリソースを表現(/users/orders
  • HTTPメソッドで操作を表現
  • 適切なHTTPステータスコードを使用
  • ページネーション、フィルタリング、ソートをサポート
  • バージョニング(/api/v1/users

gRPC(Google Remote Procedure Call)

gRPCはProtocol Buffersを使った高性能なRPCフレームワークです。

// user.proto - Protocol Buffers definition
syntax = "proto3";

service UserService {
  rpc GetUser (GetUserRequest) returns (User);
  rpc ListUsers (ListUsersRequest) returns (stream User);
}

message GetUserRequest {
  string user_id = 1;
}

message User {
  string id = 1;
  string name = 2;
  string email = 3;
}

GraphQL

GraphQLはクライアントが必要なデータだけを指定して取得できるクエリ言語です。

# Client specifies exactly what data it needs
query {
  user(id: "123") {
    name
    email
    orders {
      id
      total
      items {
        name
        price
      }
    }
  }
}

API方式の比較

特徴 REST gRPC GraphQL
プロトコル HTTP/1.1, HTTP/2 HTTP/2 HTTP/1.1, HTTP/2
データ形式 JSON, XML Protocol Buffers(バイナリ) JSON
型安全性 OpenAPI/Swagger 組み込み(.proto) スキーマ定義
ストリーミング WebSocket別途 双方向ストリーミング Subscription
パフォーマンス 高(バイナリ、HTTP/2)
学習コスト 低い 中〜高
適用場面 公開API、Web サービス間通信 複雑なデータ取得
Over-fetching あり得る なし なし
flowchart LR
    subgraph External["外部クライアント向け"]
        REST["REST API"]
        GQL["GraphQL"]
    end
    subgraph Internal["サービス間通信"]
        GRPC["gRPC"]
    end
    subgraph Async["非同期通信"]
        MQ["Message Queue"]
    end
    style External fill:#3b82f6,color:#fff
    style Internal fill:#22c55e,color:#fff
    style Async fill:#f59e0b,color:#fff

API Gatewayパターン

API Gatewayはすべてのクライアントリクエストの入口となるサーバーです。

flowchart TB
    Web["Webアプリ"]
    Mobile["モバイルアプリ"]
    Third["サードパーティ"]

    subgraph Gateway["API Gateway"]
        Auth["認証"]
        RL["レートリミティング"]
        Route["ルーティング"]
        Agg["レスポンス集約"]
        Cache["キャッシュ"]
    end

    Web --> Gateway
    Mobile --> Gateway
    Third --> Gateway

    Gateway --> S1["ユーザーサービス"]
    Gateway --> S2["商品サービス"]
    Gateway --> S3["注文サービス"]

    style Gateway fill:#3b82f6,color:#fff

API Gatewayの主な責務:

機能 説明
ルーティング リクエストを適切なサービスに転送
認証・認可 トークン検証、権限チェック
レートリミティング APIの過剰利用を防止
ロードバランシング 複数インスタンスへの負荷分散
キャッシュ レスポンスのキャッシュ
レスポンス集約 複数サービスの結果を統合
プロトコル変換 外部REST → 内部gRPC
ログ・監視 リクエストの記録と分析

代表的な実装:AWS API GatewayKongNginxEnvoy


サービスディスカバリ

マイクロサービスのインスタンスは動的に増減します。サービスディスカバリは、各サービスの場所(IP、ポート)を管理する仕組みです。

クライアントサイドディスカバリ

sequenceDiagram
    participant C as クライアント
    participant R as Service Registry
    participant S1 as Service A (Instance 1)
    participant S2 as Service A (Instance 2)

    S1->>R: 登録(IP:PORT)
    S2->>R: 登録(IP:PORT)
    C->>R: Service Aのインスタンスは?
    R->>C: [Instance 1, Instance 2]
    C->>S1: 直接リクエスト

サーバーサイドディスカバリ

sequenceDiagram
    participant C as クライアント
    participant LB as Load Balancer
    participant R as Service Registry
    participant S1 as Service A (Instance 1)

    C->>LB: リクエスト
    LB->>R: Service Aのインスタンスは?
    R->>LB: [Instance 1, ...]
    LB->>S1: リクエスト転送
    S1->>LB: レスポンス
    LB->>C: レスポンス
方式 メリット デメリット
クライアントサイド レイテンシが低い クライアントが複雑になる
サーバーサイド クライアントがシンプル Load Balancerがボトルネックになりうる

代表的なツール:ConsuletcdZooKeeperEureka


レートリミティングとスロットリング

APIを過剰な利用やDDoS攻撃から守るための仕組みです。

主なアルゴリズム

アルゴリズム 仕組み 特徴
Token Bucket トークンが一定レートで補充。リクエストごとにトークンを消費 バースト許容、最も一般的
Leaky Bucket リクエストをキューに入れ、一定レートで処理 出力が均一
Fixed Window 固定時間窓でカウント 実装が簡単だが境界問題あり
Sliding Window Log タイムスタンプをログに記録 正確だがメモリ消費大
Sliding Window Counter Fixed + Sliding の組み合わせ バランスが良い
flowchart LR
    subgraph TB["Token Bucket"]
        direction TB
        Refill["トークン補充\n(一定レート)"]
        Bucket["バケット\n[●●●○○]"]
        Refill --> Bucket
    end
    Request["リクエスト"] --> Bucket
    Bucket -->|トークンあり| Allow["許可 ✓"]
    Bucket -->|トークンなし| Deny["拒否 429"]
    style TB fill:#3b82f6,color:#fff
    style Allow fill:#22c55e,color:#fff
    style Deny fill:#ef4444,color:#fff

HTTPレスポンスヘッダー例:

HTTP/1.1 429 Too Many Requests
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1640000000
Retry-After: 30

認証:OAuth 2.0とJWT

OAuth 2.0フロー(Authorization Code)

sequenceDiagram
    participant U as ユーザー
    participant App as アプリケーション
    participant Auth as 認証サーバー
    participant API as リソースサーバー

    U->>App: ログインボタンクリック
    App->>Auth: 認可リクエスト(redirect)
    Auth->>U: ログイン画面表示
    U->>Auth: 認証情報入力
    Auth->>App: 認可コード
    App->>Auth: 認可コード + Client Secret
    Auth->>App: Access Token + Refresh Token
    App->>API: Access Token付きリクエスト
    API->>App: 保護されたリソース

JWT(JSON Web Token)

JWTは3つのパートで構成されます:

Header.Payload.Signature

// Header
{
  "alg": "HS256",
  "typ": "JWT"
}

// Payload
{
  "sub": "user123",
  "name": "Taro",
  "role": "admin",
  "exp": 1640000000
}

// Signature
HMACSHA256(
  base64UrlEncode(header) + "." + base64UrlEncode(payload),
  secret
)
方式 特徴 適用場面
Session-based サーバー側で状態管理 従来のWebアプリ
JWT ステートレス、自己完結 マイクロサービス、SPA
OAuth 2.0 第三者認可の標準 ソーシャルログイン、API認可
API Key シンプル、固定キー サーバー間通信

冪等性(Idempotency)

冪等性とは、同じ操作を何回実行しても結果が変わらない性質です。ネットワーク障害によるリトライで重要になります。

sequenceDiagram
    participant C as クライアント
    participant S as サーバー

    C->>S: POST /payment (Idempotency-Key: abc123)
    Note over S: 決済処理実行
    S--xC: レスポンス(ネットワークエラー)
    Note over C: タイムアウト → リトライ
    C->>S: POST /payment (Idempotency-Key: abc123)
    Note over S: 同じキーなので<br/>前の結果を返す
    S->>C: 200 OK(前回と同じ結果)

HTTPメソッドの冪等性:

メソッド 冪等性 安全性 説明
GET リソースの取得
PUT リソースの置換
DELETE リソースの削除
POST リソースの作成
PATCH リソースの部分更新

冪等性の実装方法:

  1. Idempotency Key: クライアントがユニークなキーを送信
  2. サーバー側チェック: キーをRedis等に保存し、重複チェック
  3. 結果キャッシュ: 処理結果を一定期間保存して返す

実践:ライドシェアサービスのAPI設計

面接での設計例を見てみましょう。

主要なAPI

# Rider APIs
POST   /api/v1/rides                    # Request a ride
GET    /api/v1/rides/{rideId}           # Get ride details
PUT    /api/v1/rides/{rideId}/cancel    # Cancel ride
GET    /api/v1/rides/{rideId}/track     # Track ride (WebSocket)

# Driver APIs
PUT    /api/v1/drivers/{id}/location    # Update location
PUT    /api/v1/drivers/{id}/status      # Update availability
GET    /api/v1/drivers/{id}/rides       # Get assigned rides
PUT    /api/v1/rides/{rideId}/accept    # Accept ride
PUT    /api/v1/rides/{rideId}/complete  # Complete ride

# Matching Service (internal gRPC)
rpc FindNearbyDrivers(Location) returns (stream Driver)
rpc AssignDriver(RideRequest) returns (Assignment)

# Payment APIs
POST   /api/v1/payments                # Process payment
GET    /api/v1/payments/{id}           # Get payment status
POST   /api/v1/payments/{id}/refund    # Process refund

サービス間アーキテクチャ

flowchart TB
    Client["モバイルアプリ"]
    subgraph GW["API Gateway"]
        Auth2["認証"]
        RL2["Rate Limit"]
    end
    Client --> GW

    subgraph Core["コアサービス"]
        Ride["配車サービス"]
        Match["マッチングサービス"]
        Driver["ドライバーサービス"]
        Payment["決済サービス"]
        Notify["通知サービス"]
    end
    GW --> Ride
    Ride -->|gRPC| Match
    Match -->|gRPC| Driver
    Ride --> Payment
    Ride --> Notify

    subgraph Data["データストア"]
        RideDB["Rides DB"]
        GeoIdx["Geospatial Index\n(Redis)"]
        PayDB["Payments DB"]
    end
    Ride --> RideDB
    Driver --> GeoIdx
    Payment --> PayDB

    style GW fill:#3b82f6,color:#fff
    style Core fill:#22c55e,color:#fff
    style Data fill:#8b5cf6,color:#fff

まとめ

今日のポイント一覧

トピック 重要ポイント
モノリス vs マイクロサービス トレードオフを理解し、状況に応じて選択
API設計 REST(公開API)、gRPC(サービス間)、GraphQL(複雑なデータ取得)
API Gateway ルーティング、認証、レートリミティングの一元管理
サービスディスカバリ 動的なサービスインスタンスの管理
レートリミティング Token Bucket が最も一般的
認証 OAuth 2.0 + JWT がマイクロサービスの標準
冪等性 ネットワーク障害時のリトライ安全性を確保

面接で使えるキーフレーズ

  • 「公開APIにはRESTを使い、サービス間通信にはgRPCを選択します」
  • 「API Gatewayで認証とレートリミティングを一元管理します」
  • 「決済APIには冪等性キーを必須にして、二重課金を防ぎます」

練習問題

基礎レベル

  1. RESTful APIの設計原則を5つ挙げ、それぞれブログ管理APIの例で説明してください
  2. Token BucketとLeaky Bucketの違いを図を描いて説明してください

中級レベル

  1. ECサイトのマイクロサービス分割を設計してください。サービス間の通信方式(同期/非同期)も含めて説明してください
  2. JWTベースの認証で、トークンが漏洩した場合の対策を3つ以上考えてください

チャレンジ

  1. ライドシェアサービスで、ドライバーの位置情報をリアルタイムで更新するAPIを設計してください。1秒ごとの位置更新を100万人のドライバーから受け取る場合のスケーラビリティを考慮してください

参考リンク


次回予告

Day 7: URL短縮サービスの設計 — いよいよ本格的なシステム設計面接の実践です。URL短縮サービスの要件定義からアーキテクチャ設計まで、面接の流れに沿って一通り設計します。