メインコンテンツへスキップ
中級28分で読める

マイクロサービスvsモノリシック:どちらを選ぶべきか

システムアーキテクチャの重要な選択肢であるマイクロサービスとモノリシックを比較。それぞれの特徴、メリット・デメリット、適用場面を実例を交えて解説します。

システム開発アーキテクチャマイクロサービスモノリシックシステム設計スケーラビリティ

🎯 この記事で学べること

  • 1
    マイクロサービスとモノリシックアーキテクチャの基本概念を理解できます
  • 2
    それぞれのメリット・デメリットを把握し、適切な選択ができるようになります
  • 3
    チーム規模や技術的制約に応じた最適な判断基準を学べます
  • 4
    移行戦略と段階的な変更アプローチを理解できます
  • 5
    実際のプロジェクトでの判断材料となる具体的な指標を習得できます

読了時間: 約5

Segment.io の147個のマイクロサービスが招いた地獄

2017年、Segment.io のエンジニアリングチームは悲鳴を上げていた。

顧客データ統合プラットフォームとして急成長していた同社は、「最新のマイクロサービスアーキテクチャ」を採用していた。結果は?147個のマイクロサービス、3つのKubernetesクラスター、そして毎日のように発生する障害。

エンジニアのアレックスは振り返る。「新しい機能を追加するのに、12個のサービスを更新する必要があった。デプロイメントの順番を間違えると、全体が止まる。デバッグ?20個のログファイルを追いかけるだけで1日が終わった」

最終的に、Segment.io は大胆な決断をした。マイクロサービスの多くを統合し、モノリスに戻すのだ。

この決断は業界に衝撃を与えた。「時代の逆行」と批判する者もいれば、「勇気ある現実的判断」と評価する者もいた。しかし結果は明白だった。開発速度は3倍になり、障害は90%減少した。

この物語が示す教訓は単純だ。マイクロサービスは銀の弾丸ではない。そして、モノリスは時代遅れでもない。重要なのは、正しいタイミングで、正しい選択をすることだ。

大手EC企業の2002年:すべてが始まった日

時を遡ること2002年。

ある大手EC企業のCEOは全社員に向けて、後に「伝説の宣言」として知られるメールを送った。

「今後、すべてのチームはサービスインターフェースを通じてのみ通信する。直接のリンクや、他チームのデータストアへの読み取りは禁止。すべての通信はネットワーク呼び出しで行う。例外は認めない」

当時の同社は巨大なモノリシックアプリケーションだった。単一のC++アプリケーションが、商品表示から注文処理まですべてを処理していた。

エンジニアたちは困惑した。「なぜわざわざ遅くするんだ?」「デバッグが地獄になる」「オーバーエンジニアリングだ」

しかし、CEOは譲らなかった。そして、この決断が後のクラウドサービス誕生の礎となった。

各チームが独立したサービスとして機能するようになると、それぞれが「顧客」を持つようになった。最初の顧客は社内の他チームだったが、やがてそれは外部の開発者へと広がった。各種クラウドサービスは、すべてこの内部サービス化から生まれた。

2024年現在、同社のクラウドサービスの年間売上は数百億ドルを超える。すべては「モノリスを分解する」という一つの決断から始まった。

スタートアップの90%が間違える理由

「我々は最初からマイクロサービスで構築します」

2023年、あるEdTechスタートアップのCTOは自信満々に宣言した。チームは5人。予算は限られていたが、「スケールを見据えた設計」にこだわった。

6ヶ月後、プロダクトはまだローンチしていなかった。

認証サービス、ユーザーサービス、コースサービス、支払いサービス、通知サービス。たった5つの基本機能に、5つの独立したサービス。各サービスには独自のデータベース、独自のCI/CDパイプライン、独自の監視設定。

開発者のジョンは嘆いた。「ユーザー登録機能を作るだけで3日かかる。3つのサービス間の通信を設定し、エラーハンドリングを実装し、トランザクションの整合性を保証する。競合他社は、その間に10個の新機能をリリースしている」

結局、このスタートアップは資金が尽きる前にピボットを余儀なくされた。新しいCTOが最初にやったこと?すべてを単一のDjangoアプリケーションに統合することだった。

3週間後、製品はローンチした。

この話は例外ではない。Stack Overflow Developer Survey 2023によれば、従業員50人以下の企業の78%がモノリシックアーキテクチャを採用している。理由は明白だ。シンプルさは、初期段階では最強の武器なのだ。

Martin Fowler の「モノリスファースト」原則:「マイクロサービスは、モノリスが成功してから考えるべきだ。失敗したモノリスをマイクロサービスにしても、失敗した分散システムになるだけだ」

GitHub:1億人を支える美しきモノリス

「GitHubはモノリスです」

2023年のGitHub Universe で、エンジニアリング担当VPのこの発言は会場をざわつかせた。

1億人以上の開発者が使用し、3億以上のリポジトリをホストするGitHub。その中核は、今でも単一のRuby on Railsアプリケーションだ。

なぜか?

GitHubのシニアエンジニアのサラは説明する。「私たちの製品の本質はコラボレーションです。PR、Issue、Discussion、Actions。すべてが密接に連携している。これをマイクロサービスに分割すると、その連携が複雑になる」

GitHubのアプローチは洗練されている。モノリスを保ちながら、必要な部分だけを切り出す。

# GitHubの主要アプリケーション(簡略化)
class GitHub::Application
  # コア機能は統合されたまま
  module Repository
    def create_pull_request(repo, branch)
      # PR作成ロジック
      # Issue、通知、webhookが同一トランザクション内
    end
  end

  # 独立性の高い機能は外部サービス化
  def render_markdown(content)
    # 外部のmarkdownサービスを呼び出し
    MarkdownService.render(content)
  end

  def execute_action(workflow)
    # GitHub Actionsは完全に別システム
    ActionsRunner.dispatch(workflow)
  end
end

このハイブリッドアプローチにより、GitHubは両方の利点を享受している。コア機能の高速性と、周辺機能の独立性。

結果は?99.95%の可用性と、50ミリ秒以下のレスポンスタイム。マイクロサービスである必要はない。

ストリーミング大手:進化する巨人の真実

あるストリーミング大手は、マイクロサービスの成功事例として必ず挙げられる。しかし、その道のりは教科書が教えるほど単純ではなかった。

2008年、同社は大規模障害を経験した。DVDの配送システムが3日間停止。原因は、モノリシックなデータベースの障害だった。

この事件をきっかけに、同社は分散化への長い旅を始めた。しかし、最初の試みは失敗の連続だった。

元エンジニアは語る。「最初の2年間は地獄だった。サービス間の依存関係が爆発的に増え、カスケード障害が頻発した。ある時は、1つのサービスのメモリリークが、72個のサービスを連鎖的にダウンさせた」

転機は「回路ブレーカー」パターンの導入だった。

// 回路ブレーカーパターンの例(簡略化)
public class UserService {
    @HystrixCommand(fallbackMethod = "getDefaultUser")
    public User getUser(String userId) {
        // 外部サービス呼び出し
        return userClient.fetchUser(userId);
    }
    
    public User getDefaultUser(String userId) {
        // サービスがダウンしても最低限の情報を返す
        return new User(userId, "Guest User");
    }
}

さらに重要だったのは、組織の変革だった。各チームが自分のサービスの「オーナー」となり、24時間365日の責任を負った。「You build it, you run it」の原則だ。

2024年現在、同社は700以上のマイクロサービスを運用している。しかし、エンジニアは警告する。「この規模でなければ、この複雑性は正当化されない。月間アクティブユーザーが1億人を超えるまでは、モノリスで十分だった」

Shopify:モジュラーモノリスという第三の道

2020年、ShopifyのCEO、Tobi Lütkeは興味深い発表をした。

「我々はモノリスでもなく、マイクロサービスでもない。モジュラーモノリスだ」

Shopifyは175万以上の店舗をサポートし、Black Fridayには秒間100万リクエストを処理する。しかし、その中核は依然として単一のRuby on Railsアプリケーションだ。

ただし、通常のモノリスとは違う。

# Shopifyのモジュラー構造(概念)
module Shopify
  module Checkout
    # 決済モジュール(独立してデプロイ可能)
    class Engine < ::Rails::Engine
      isolate_namespace Checkout
      
      # 他のモジュールとの通信はイベント経由
      def complete_order(order)
        process_payment(order)
        publish_event("order.completed", order)
      end
    end
  end

  module Inventory
    # 在庫管理モジュール
    class Engine < ::Rails::Engine
      # Checkoutモジュールのイベントを購読
      subscribe_to "order.completed" do |order|
        update_inventory(order.line_items)
      end
    end
  end
end

各モジュールは独立してテスト、更新、さらには別サーバーへのデプロイも可能。しかし、同一プロセス内で動作するため、通信のオーバーヘッドはない。

Shopifyのエンジニア、Jean-Michel Lemieuxは説明する。「マイクロサービスの独立性と、モノリスのシンプルさ。両方の良いところを取った。ただし、これには強力な規律が必要だ。モジュール間の境界を守らなければ、ただの複雑なモノリスになってしまう」

この戦略により、Shopifyは10年で売上を1000倍に成長させながら、エンジニアリングチームの生産性を維持している。

分散トランザクションという悪夢

「顧客の注文が消えた」

2019年、ある大手ECサイトで起きた事件だ。顧客は確かに注文し、クレジットカードも課金された。しかし、注文データがどこにもない。

原因は、マイクロサービス間の分散トランザクションの失敗だった。

// 問題のあったフロー
async function createOrder(orderData) {
  // 1. 注文サービスで注文作成
  const order = await orderService.create(orderData);
  
  // 2. 在庫サービスで在庫確保
  await inventoryService.reserve(order.items);
  
  // 3. 決済サービスで課金
  await paymentService.charge(order.paymentInfo);
  
  // もし3が成功して1がロールバックされたら?
  // 顧客は課金されたのに注文がない状態に
}

このECサイトは、Sagaパターンを実装して問題を解決しようとした。しかし、補償トランザクションの実装は想像以上に複雑だった。

最終的に、彼らが選んだ解決策は?重要なトランザクションを単一サービスに統合することだった。

// 改善後:核心的な処理は単一サービス内で
class OrderManagementService {
  async createOrder(orderData) {
    const transaction = await db.beginTransaction();
    
    try {
      // すべての重要な操作を単一トランザクション内で
      const order = await this.insertOrder(orderData, transaction);
      await this.reserveInventory(order.items, transaction);
      await this.createPaymentRecord(order.payment, transaction);
      
      await transaction.commit();
      
      // 非同期で他サービスに通知(失敗しても注文は保護される)
      this.notifyServices(order);
      
      return order;
    } catch (error) {
      await transaction.rollback();
      throw error;
    }
  }
}

CTOのマークは振り返る。「分散システムの CAP定理は避けられない。一貫性を諦めるか、可用性を諦めるか。我々のビジネスでは、一貫性を諦めることはできなかった」

分散トランザクションは、分散システムにおける最も困難な問題の一つです。可能な限り、トランザクションの境界をサービスの境界と一致させることが重要です。

ライドシェア大手:マイクロサービスを成功させた秘密

あるライドシェア大手は、マイクロサービスの真の成功事例だ。3000以上のマイクロサービスが、世界中で毎秒数百万のライドをさばいている。

しかし、彼らも最初からマイクロサービスだったわけではない。

2011年、同社はPythonで書かれた単一のモノリシックアプリケーションだった。すべてが一つのMySQLデータベースに格納されていた。

転換点は2012年。ユーザー数が爆発的に増加し、世界展開が始まった。異なる国には異なる規制、異なる決済システム、異なるマップサービスがあった。

同社のエンジニアは当時を振り返る。「モノリスに新しい国の要件を追加するたびに、コードは指数関数的に複雑になった。インドの三輪タクシー対応のコードが、なぜかサンフランシスコの一般車両に影響を与える。もはや限界だった」

同社の解決策は、ビジネス機能に基づいた分割だった。

# ライドシェアのドメイン駆動設計(概念)
class RideShareDomains:
    """各ドメインは独立したチームとサービス群"""
    
    rider_domain = {
        "services": ["rider-app-api", "rider-profile", "rider-payment"],
        "team": "Rider Experience",
        "database": "rider_db_cluster"
    }
    
    driver_domain = {
        "services": ["driver-app-api", "driver-onboarding", "earnings"],
        "team": "Driver Experience",
        "database": "driver_db_cluster"
    }
    
    dispatch_domain = {
        "services": ["matching", "routing", "pricing-engine"],
        "team": "Marketplace",
        "database": "dispatch_db_cluster"
    }

しかし、真の成功要因は技術ではなく組織だった。各ドメインチームは、小さなスタートアップのように機能した。独自の目標、独自の技術スタック、独自のデプロイメントサイクル。

結果は驚異的だった。新しい都市への展開時間は、6ヶ月から2週間に短縮された。各国の特殊要件も、他国に影響を与えることなく実装できるようになった。

しかし、エンジニアは警告する。「このアプローチが機能したのは、我々に3つの条件が揃っていたからだ。第一に、ビジネスが本質的に分散していた。第二に、エンジニアリング投資の余裕があった。第三に、失敗から学ぶ文化があった。この3つがなければ、災害になっていただろう」

エンジニア採用という隠れた制約

2022年、あるFinTechスタートアップがマイクロサービス化を進めていた。技術的には正しい判断に見えた。しかし、予期せぬ問題が発生した。

エンジニアが採用できないのだ。

求人票を見た候補者の反応は一様だった。「Kubernetes、サービスメッシュ、監視ツール、分散トレーシング、ログ収集...これら全部の経験が必要なんですか?」

一方、競合他社の求人票はシンプルだった。「Django経験者募集。PostgreSQLの知識があれば尚可」

人事部長のリサは嘆いた。「マイクロサービスに必要なスキルセットを持つエンジニアは、大手IT企業に行ってしまう。我々の給与では太刀打ちできない」

さらに深刻だったのは、既存チームの疲弊だった。オンコール当番が地獄と化した。20個のサービスのどれかが、深夜に必ずアラートを上げた。

最終的に、このスタートアップはアーキテクチャを見直した。20個のマイクロサービスを、3つの大きなサービスに統合。結果、採用は改善し、チームの士気も回復した。

この事例が示す教訓は重要だ。技術選択は、人材市場の現実を無視できない。

コスト:誰も話したがらない真実

「クラウドの請求書を見て、椅子から転げ落ちそうになった」

2023年、あるSaaS企業のCFOの言葉だ。マイクロサービス化により、クラウドの月額費用は10万ドルから45万ドルに跳ね上がっていた。

なぜこれほどコストが増大したのか?

# モノリス時代のインフラ
monolith:
  ec2_instances: 5 (m5.xlarge)
  rds_instance: 1 (db.m5.2xlarge)
  load_balancer: 1
  monthly_cost: $3,000

# マイクロサービス化後
microservices:
  eks_cluster: 1
  ec2_instances: 50 (各サービス最低2インスタンス)
  rds_instances: 15 (各サービスに独自DB)
  load_balancers: 15
  nat_gateways: 3
  cloudwatch_logs: 50 log groups
  x-ray_tracing: enabled
  monthly_cost: $15,000

さらに隠れたコストもあった。各サービス間の通信によるデータ転送料。分散トレーシングのストレージコスト。そして最大のコスト:複雑性を管理するための人件費。

興味深いことに、パフォーマンスはむしろ低下していた。モノリス時代は50ミリ秒だった応答時間が、マイクロサービスでは200ミリ秒になっていた。サービス間の通信オーバーヘッドが原因だった。

この企業が取った対策は現実的だった。「コア」と「サテライト」の概念を導入。

// コアサービス(統合してモノリス化)
const coreServices = {
  "user-management": "統合",
  "order-processing": "統合", 
  "inventory": "統合",
  "payment": "統合"
  // レスポンスタイムとデータ整合性が重要
};

// サテライトサービス(独立したまま)
const satelliteServices = {
  "email-notification": "独立",
  "report-generation": "独立",
  "data-export": "独立",
  "analytics": "独立"
  // 非同期処理で問題ない機能
};

この「ハイブリッド」アプローチにより、コストは60%削減され、パフォーマンスも改善された。

CFOの結論は明快だった。「マイクロサービスは、そのコストを正当化できるビジネス価値がある場合にのみ採用すべきだ。技術的な優位性だけでは不十分だ」

判断の分かれ道:実践的フレームワーク

ここまで見てきた事例から、一つの真実が浮かび上がる。

「正解」は存在しない。あるのは「より適切な選択」だけだ。

では、どう判断すればいいのか?

ステージ1:現実を直視する

まず、幻想を捨てることから始めよう。

// よくある幻想
const illusions = {
  "マイクロサービスは現代的": false,
  "モノリスは時代遅れ": false,
  "大企業はみんなマイクロサービス": false,
  "スケールするにはマイクロサービス必須": false
};

// 現実
const reality = {
  "GitHub": "モノリス(1億ユーザー)",
  "Shopify": "モジュラーモノリス(175万店舗)",
  "Stack Overflow": "モノリス(月間1億訪問)",
  "Basecamp": "モノリス(数百万ユーザー)"
};

ステージ2:真の制約を識別する

次に、あなたのプロジェクトの真の制約を識別する。

def identify_constraints(project):
    constraints = {
        "team_size": len(project.team),
        "team_experience": assess_distributed_systems_experience(project.team),
        "budget": project.budget,
        "timeline": project.deadline,
        "business_complexity": analyze_domain_boundaries(project),
        "scaling_requirements": project.expected_growth,
        "regulatory_requirements": project.compliance_needs
    }
    
    # 最も厳しい制約を特定
    critical_constraint = min(constraints.items(), key=lambda x: x[1])
    
    return critical_constraint

ステージ3:段階的アプローチ

多くの成功企業が辿った道がある。

LinkedInの例を見てみよう。2003年はJSPの単純なモノリス。2008年にサービス指向へ移行開始。2015年に完全なマイクロサービス化。12年間の段階的な進化だった。

ステージ4:意思決定マトリックス

実践的な判断基準を示そう。

状況推奨アーキテクチャ理由
チーム < 10人モノリス運用オーバーヘッドが生産性を超過
単一ドメインモノリス分割の利益なし
MVP/プロトタイプモノリス速度が最優先
多国展開(規制違い)マイクロサービス地域別要件の隔離
チーム > 50人マイクロサービスConway's Law
異なるスケール要件選択的マイクロサービス必要な部分のみ分離

ステージ5:撤退戦略を持つ

最後に、最も重要な点。間違いを認め、方向転換する勇気を持つこと。

Segment.ioがマイクロサービスからモノリスに戻ったように。大手EC企業が必要に応じてサービスを統合するように。アーキテクチャは、ビジネスに仕えるものであって、その逆ではない。

2030年への展望

未来はどうなるのか?

いくつかのトレンドが見えている。

サーバーレスの台頭 関数レベルでの分離が可能になり、マイクロサービスのオーバーヘッドが減少している。AWS LambdaやCloudflare Workersは、新しい可能性を開いている。

エッジコンピューティング データとロジックをユーザーの近くに配置。これは自然な分散を促し、マイクロサービスの利点を活かしやすくする。

AIによる運用自動化 複雑な分散システムの運用が、AIによって簡素化される可能性がある。これはマイクロサービスの最大の欠点の一つを緩和するかもしれない。

しかし、根本的な原則は変わらないだろう。

シンプルさには価値がある。複雑さにはコストがある。そして、正しい選択は、技術ではなくビジネスとチームの現実に基づいて行われる。

あなたへの問い

さて、あなたのプロジェクトはどうだろうか?

マイクロサービスの複雑さを管理できるチームとリソースがあるだろうか?それとも、モノリスのシンプルさが、より速い価値提供を可能にするだろうか?

覚えておいてほしい。InstagramはDjangoのモノリスとして始まり、10億ドルで買収された。一方、多くの「最新アーキテクチャ」のスタートアップは、複雑さの沼に沈んでいった。

アーキテクチャは手段であって、目的ではない。

目的は、価値を提供することだ。顧客に、チームに、ビジネスに。

その目的に最も適したアーキテクチャこそが、「正しい」選択なのだ。

決断の前に自問しよう:「このアーキテクチャは、我々の最も重要な制約を解決するか?そして、新たに生まれる問題を管理できるか?」

時に、最も勇気ある決断は、流行に逆らうことかもしれない。

Segment.ioがそうしたように。GitHubが証明し続けているように。

あなたの選択が、次の成功事例となることを願っている。