mybook

マイクロサービスとは何か — 定義と原則

「マイクロサービスって、要するに小さいサービスに分けることですよね?」

会議が終わったあと、新人エンジニアのケンジがミサキに聞いた。

「半分正解。でも『小さい』ことより『独立している』ことの方が重要なんだよね」


マイクロサービスの定義

マイクロサービスアーキテクチャとは、1つのアプリケーションを、独立してデプロイ可能な小さなサービスの集合として構築するスタイルだ。

各サービスは:

  • 独自のプロセスで動作する(別のサーバー、別のコンテナ)
  • 軽量な通信メカニズム(HTTP/gRPC/メッセージキュー)で連携する
  • ビジネス機能に沿って設計される(技術レイヤーではなく)
  • 独立してデプロイ・スケール・障害からの回復ができる
Loading diagram...

モノリスとの根本的な違い

デプロイの独立性

これがマイクロサービスで最も重要な特性だ。

# モノリスのデプロイ
$ git push origin main
# → アプリ全体が再起動
# → 商品機能の修正でも、注文機能のコードも再デプロイされる
# → ダウンタイムリスクが全機能に影響
 
# マイクロサービスのデプロイ
$ git push product-service main
# → 商品サービスだけが再デプロイ
# → 注文サービスは影響なく動作し続ける
# → ダウンタイムリスクは商品機能のみ

障害の分離

# モノリスでの障害伝播(疑似コード)
class SearchService
  def search(query)
    # 検索DBが重くなると...
    results = SlowSearchDB.query(query)  # タイムアウト30秒
    # → このリクエストを処理するスレッドが30秒間ブロック
    # → スレッドプールが枯渇
    # → 注文処理のリクエストも捌けなくなる
    # → サイト全体が停止
  end
end
# マイクロサービスでの障害分離
# 検索サービスが落ちても...
class OrdersController < ApplicationController
  def create
    # サーキットブレーカーが検索サービスの障害を検知
    # → 注文処理は独立して継続
    order = OrderService.create(params)
    render json: order, status: :created
  end
end

スケールの独立性

# ECS サービス定義 — 機能ごとに独立したスケール設定
# 検索サービス: セールで検索が集中するのでスケールアウト
search-service:
  desired_count: 20
  cpu: 1024
  memory: 2048
 
# 注文サービス: 通常のスケール
order-service:
  desired_count: 5
  cpu: 512
  memory: 1024
 
# ユーザーサービス: 軽量
user-service:
  desired_count: 3
  cpu: 256
  memory: 512

マイクロサービスの4つの原則

原則1: 単一責任(Single Responsibility)

各サービスは1つのビジネスドメインに集中する。

# 悪い例: 責任が混在したサービス
class ProductAndOrderAndUserService
  def create_order(user_id, product_id, quantity)
    user = fetch_user(user_id)           # ユーザーの責任
    product = fetch_product(product_id)  # 商品の責任
    order = create_order_record(...)     # 注文の責任
    send_email(user, order)              # 通知の責任
  end
end
 
# 良い例: 責任が明確
class OrderService
  def create_order(user_id, product_id, quantity)
    # 注文の作成だけに集中
    # ユーザー情報はUser Serviceから取得(API経由)
    # 商品情報はProduct Serviceから取得(API経由)
    # 通知はNotification Serviceに委譲(イベント経由)
    order = Order.create!(...)
    publish_event('order_created', order)
    order
  end
end

原則2: 独立デプロイ(Independent Deployability)

サービスAの変更がサービスBの再デプロイを要求しない。

# 悪い例: 密結合した設定
# order-service は product-service の内部実装に依存
order_service:
  environment:
    PRODUCT_DB_HOST: product-postgres
    PRODUCT_DB_PORT: 5432
    PRODUCT_TABLE_NAME: products_v2
 
# 良い例: インターフェースのみに依存
order_service:
  environment:
    PRODUCT_SERVICE_URL: http://product-service
    PRODUCT_SERVICE_VERSION: v1

原則3: データの分散所有(Decentralized Data Management)

各サービスは自分のデータを所有し、他サービスのDBに直接アクセスしない。

# 絶対にやってはいけないこと
module OrderService
  class Order < ApplicationRecord
    # OrderServiceのコードがProductServiceのDBに直接アクセス
    def product
      ProductServiceDatabase::Product.find(product_id)
    end
  end
end
 
# 正しい方法: APIを通じてアクセス
module OrderService
  class Order < ApplicationRecord
    def product
      ProductServiceClient.find(product_id)
    end
  end
end

原則4: 障害に備えた設計(Design for Failure)

依存サービスが落ちることを前提にコードを書く。

# app/services/product_service_client.rb
class ProductServiceClient
  def self.find(product_id)
    response = HTTP.timeout(2).get("#{BASE_URL}/products/#{product_id}")
    JSON.parse(response.body)
  rescue HTTP::TimeoutError, HTTP::ConnectionError => e
    # サービスが落ちていても graceful degradation
    Rails.logger.error("ProductService unavailable: #{e.message}")
    { id: product_id, name: '商品情報取得中', price: 0 }  # フォールバック
  end
end

「マイクロ」のサイズ感

「マイクロ」という名前に惑わされてはいけない。重要なのはコード行数ではなく、ビジネスの境界だ。

INFO

マイクロサービスの適切なサイズ感のヒント:「1つのスクワッド(4〜8人)が所有・開発できる規模」。コード行数でなく、チームの認知負荷で測る。

小さすぎるサービス(ナノサービス)の問題:
  UserEmailService  ← メール変更だけ
  UserPasswordService ← パスワードだけ
  UserNameService   ← 名前だけ
→ ネットワーク呼び出しが爆発、管理コストが高騰

適切なサービス:
  UserService       ← ユーザーの全属性、認証、プロフィール
  OrderService      ← 注文ライフサイクル全体
  ProductService    ← 商品カタログ、在庫
→ ビジネスの変更が1つのサービス内で完結

モノリシックファーストの原則

WARNING

新規プロジェクトには最初からマイクロサービスを採用しないこと。ドメインの理解が不十分な状態では、誤った境界を引いてしまう。まずモノリスで作り、境界が見えてから分割する。

Martin Fowler が提唱する「モノリシックファースト」のアプローチ:

Phase 1: モノリスで MVP を作る
  → ドメインの理解を深める
  → どこで変更が頻発するかを観察

Phase 2: 明確な境界が見えてきたら分割
  → 変更頻度が異なる領域
  → チームの所有権が分かれる領域
  → スケール要件が異なる領域

Phase 3: 継続的に進化させる
  → サービスはいつでも統合・分割できる

ShopNovaの現状整理

ミサキはチームとともに、ShopNovaのドメインを整理した。

Loading diagram...

それぞれのドメインは:

ドメイン変更頻度スケール要件所有チーム
商品管理Squad A
注文処理高(セール時)Squad B
ユーザー管理Squad B
検索超高(セール時)Squad A
通知Squad C
レコメンドSquad C

「変更頻度が違う、スケール要件が違う、所有チームが違う — これがサービス境界のヒントだ」とミサキは言った。

次章では、この境界をどう正確に引くかを学ぶ。


まとめ

マイクロサービスの本質は「小さいこと」ではなく「独立していること」だ。

  • 独立デプロイ — 他のサービスを止めずに変更できる
  • 単一責任 — 1つのビジネスドメインに集中
  • 分散データ — 各サービスがデータを所有
  • 障害前提 — 依存サービスの障害を前提に設計

そして最も重要な教訓:最初からマイクロサービスを選ばない。ドメインが見えてから分割する。

ミサキはこの原則をホワイトボードに書き留めた。これが彼女たちの羅針盤になる。