マイクロサービスアーキテクチャは設計の選択肢が多く、サービス分割・通信プロトコル・共有型管理・テスト戦略のどれをとっても正解が一つではありません。Claude Codeはこの複雑な設計判断を支援する強力なパートナーです。DDDに基づくBounded Context分析、gRPCスキーマの自動生成、サービス間の契約テスト設計まで、アーキテクチャレベルの意思決定から実装まで一気通貫でカバーできます。
この記事では、TypeScript + Connect RPC(gRPC互換)+ Turborepo + Pactを組み合わせたマイクロサービスの設計・実装をClaude Codeで行う実践的なワークフローを解説します。
サービス分割をClaude Codeに設計させる
マイクロサービスで最も重要な設計判断は「どこでサービスを分割するか」です。Claude CodeのPlan Modeを使って、コードに触れずにアーキテクチャ分析を行います。
# Plan Modeで起動(Shift+Tab 2回) > このモノリスのソースコードを分析し、DDDのBounded Contextを特定してください。 分析手順: 1. ドメインモデルの洗い出し(エンティティ・値オブジェクト・集約) 2. ユビキタス言語の検出(同じ用語が異なる意味で使われている箇所) 3. コンテキスト境界の特定(データの所有権、トランザクション境界) 4. コンテキスト間の関係(ACL、共有カーネル、公開ホストサービス等) 出力: - 各Bounded Contextの名前・責務・所有エンティティ - コンテキスト間の依存関係図 - 段階的な分割順序の提案(ストラングラーフィグパターン)
ストラングラーフィグパターンでの段階的分割
一度にすべてを分割するのではなく、モノリスの前にファサードを配置し、機能単位で段階的に新サービスへ移行します。問題が発生すれば即座にモノリスにフォールバックできます。
既存モノリスのコードベースを分析し、 ストラングラーフィグパターンで段階的に分割する計画を立ててください。 分割候補の評価基準: - 変更頻度(git logから分析) - 結合度(import/require の依存分析) - スケーリング要件の違い 出力: 分割フェーズ(Phase 1〜3)とそれぞれのリスク・ロールバック手順
Plan Modeの詳しい使い方はClaude Code Plan Mode完全ガイドをご覧ください。
Connect RPC(gRPC互換)でサービス間通信を実装する
サービス間通信にはConnect RPC(gRPC互換のTypeScriptフレームワーク)を使います。.protoファイルから型安全なクライアント・サーバーコードを自動生成でき、HTTP/JSONとgRPCの両方をサポートします。
.protoファイルの定義
注文サービスのgRPCスキーマ(.protoファイル)を定義してください。 エンドポイント: - CreateOrder: 注文作成 - GetOrder: 注文取得 - ListOrders: 注文一覧(ページネーション付き) - UpdateOrderStatus: ステータス更新 要件: - proto3構文 - パッケージ: acme.order.v1 - ページネーション: cursor方式
syntax = "proto3";
package acme.order.v1;
message Order {
string order_id = 1;
string customer_id = 2;
repeated OrderItem items = 3;
OrderStatus status = 4;
string created_at = 5;
}
message OrderItem {
string product_id = 1;
int32 quantity = 2;
int64 price_cents = 3;
}
enum OrderStatus {
ORDER_STATUS_UNSPECIFIED = 0;
ORDER_STATUS_PENDING = 1;
ORDER_STATUS_CONFIRMED = 2;
ORDER_STATUS_SHIPPED = 3;
ORDER_STATUS_CANCELLED = 4;
}
message CreateOrderRequest {
string customer_id = 1;
repeated OrderItem items = 2;
}
message CreateOrderResponse {
Order order = 1;
}
message GetOrderRequest {
string order_id = 1;
}
message ListOrdersRequest {
string customer_id = 1;
string cursor = 2;
int32 limit = 3;
}
message ListOrdersResponse {
repeated Order orders = 1;
string next_cursor = 2;
}
service OrderService {
rpc CreateOrder(CreateOrderRequest) returns (CreateOrderResponse);
rpc GetOrder(GetOrderRequest) returns (Order);
rpc ListOrders(ListOrdersRequest) returns (ListOrdersResponse);
}
bufでTypeScriptコードを自動生成する
version: v2
inputs:
- directory: .
plugins:
- local: protoc-gen-es
out: src/gen
include_imports: true
opt: target=ts
# buf でTypeScriptコードを生成 cd packages/proto npx buf generate # 生成されるファイル: # src/gen/acme/order/v1/order_pb.ts ← メッセージ型 + サービス定義
protoc-gen-es1つでメッセージ定義とサービス定義の両方を生成します。旧来のprotoc-gen-connect-esは廃止されました。サーバー実装(Fastify + Connect)
import { fastify } from "fastify";
import { fastifyConnectPlugin } from "@connectrpc/connect-fastify";
import type { ConnectRouter } from "@connectrpc/connect";
import { ConnectError, Code } from "@connectrpc/connect";
import { OrderService } from "@acme/proto/acme/order/v1/order_pb";
import { db } from "./db";
const routes = (router: ConnectRouter) =>
router.service(OrderService, {
async createOrder(req) {
const orderId = crypto.randomUUID();
const order = await db.insert("orders", {
orderId,
customerId: req.customerId,
items: req.items,
status: 1, // PENDING
createdAt: new Date().toISOString(),
});
return { order };
},
async getOrder(req) {
const order = await db.findById("orders", req.orderId);
if (!order) throw new ConnectError("注文が見つかりません", Code.NotFound);
return order;
},
async listOrders(req) {
const { items, nextCursor } = await db.paginate("orders", {
customerId: req.customerId,
cursor: req.cursor,
limit: req.limit || 20,
});
return { orders: items, nextCursor };
},
});
const server = fastify();
await server.register(fastifyConnectPlugin, { routes });
await server.listen({ host: "0.0.0.0", port: 50052 });
console.log("Order service running on :50052");
クライアント実装(他サービスからの呼び出し)
import { createClient } from "@connectrpc/connect";
import { createConnectTransport } from "@connectrpc/connect-node";
import { OrderService } from "@acme/proto/acme/order/v1/order_pb";
const transport = createConnectTransport({
baseUrl: process.env.ORDER_SERVICE_URL || "http://localhost:50052",
httpVersion: "1.1",
});
export const orderClient = createClient(OrderService, transport);
// 使用例: 型安全にRPCを呼び出し
// const { order } = await orderClient.createOrder({
// customerId: "cust-123",
// items: [{ productId: "prod-456", quantity: 2, priceCents: 1500n }],
// });
| 通信パターン | gRPC(Connect) | REST |
|---|---|---|
| サービス間通信 | 最適(型安全・高速) | OK |
| ブラウザ直接通信 | Connect Protocolで可能 | 最適 |
| ストリーミング | ネイティブ対応 | SSE/WebSocket別途 |
| 外部公開API | 非推奨 | 最適 |
Turborepoモノレポでマイクロサービスを管理する
microservices-platform/
├── CLAUDE.md # システム全体のルール
├── turbo.json
├── pnpm-workspace.yaml
├── apps/
│ ├── user-service/ # 認証・ユーザー管理
│ │ ├── CLAUDE.md # サービス固有ルール
│ │ ├── Dockerfile
│ │ └── src/
│ ├── order-service/ # 注文処理
│ │ ├── CLAUDE.md
│ │ ├── Dockerfile
│ │ └── src/
│ └── payment-service/ # 決済処理
│ ├── CLAUDE.md
│ ├── Dockerfile
│ └── src/
├── packages/
│ ├── proto/ # 共有.protoファイル + 生成TypeScript
│ └── shared/ # 共有ユーティリティ(ロガー、エラーハンドリング等)
└── infra/
└── docker-compose.yml
Docker Composeでローカル開発環境を構築する
services:
postgres:
image: postgres:16-alpine
environment:
POSTGRES_USER: dev
POSTGRES_PASSWORD: dev
POSTGRES_DB: platform
ports: ["5432:5432"]
healthcheck:
test: ["CMD-SHELL", "pg_isready -U dev"]
interval: 5s
timeout: 3s
retries: 5
user-service:
build:
context: ../
dockerfile: apps/user-service/Dockerfile
ports: ["50051:50051"]
environment:
DATABASE_URL: postgres://dev:dev@postgres:5432/platform
depends_on:
postgres:
condition: service_healthy
order-service:
build:
context: ../
dockerfile: apps/order-service/Dockerfile
ports: ["50052:50052"]
environment:
DATABASE_URL: postgres://dev:dev@postgres:5432/platform
USER_SERVICE_URL: http://user-service:50051
depends_on:
user-service:
condition: service_healthy
depends_onはcondition: service_healthyを必ず指定してください。デフォルト(service_started)ではコンテナの起動のみでプロセスの準備完了を待ちません。ヘルスチェックなしだとサービスの起動順序が保証されません。CLAUDE.mdテンプレート(マイクロサービス用)
# Platform - マイクロサービスアーキテクチャ ## サービス一覧 | サービス | ポート | 責務 | |---------|--------|------| | user-service | 50051 | 認証・ユーザー管理 | | order-service | 50052 | 注文処理 | | payment-service | 50053 | 決済処理 | ## 設計原則 - Database per Service: 各サービスが専用スキーマを持つ - APIファースト: .protoファイルを先に定義し、実装は後 - サービス間通信: Connect RPC(gRPC互換) ## 禁止事項 - サービス間のDB直接参照 - 循環依存(A→B→A) - 共有ライブラリへのビジネスロジック配置 ## コマンド - pnpm dev: 全サービス起動 - pnpm generate: proto → TypeScript生成 - pnpm test:contract: Pact契約テスト実行 - docker compose -f infra/docker-compose.yml up: Docker環境起動
# Order Service ## 責務: 注文の作成・更新・キャンセル ## Bounded Context: Ordering ## 依存サービス - user-service: 注文者情報の参照 - payment-service: 決済処理の依頼 ## DB: PostgreSQL (orderスキーマ) 主要テーブル: orders, order_items ## 作業スコープ - 変更可能: apps/order-service/ のみ - packages/proto/ の変更が必要な場合は確認を求める
Pactでサービス間の契約テストを自動化する
マイクロサービスでは、あるサービスのAPIを変更したとき、依存するサービスが壊れないことを保証する必要があります。Pact(Consumer-Driven Contract Testing)は、消費者側がテストでコントラクトを生成し、提供者側がそれを検証する仕組みです。
PaymentServiceからOrderServiceへの契約テストを Pact V4 + Vitest で実装してください。 テストケース: 1. 注文IDで注文を取得できる(正常系) 2. 存在しない注文IDで404エラー(異常系) Consumer: PaymentService Provider: OrderService
import { describe, it, expect } from "vitest";
import { PactV4, SpecificationVersion } from "@pact-foundation/pact";
const provider = new PactV4({
consumer: "PaymentService",
provider: "OrderService",
spec: SpecificationVersion.SPECIFICATION_VERSION_V4,
dir: "./pacts",
});
describe("OrderService Contract", () => {
it("注文詳細を取得できる", async () => {
await provider
.addInteraction()
.given("注文ID order-123 が存在する")
.uponReceiving("注文詳細の取得リクエスト")
.withRequest("GET", "/orders/order-123")
.willRespondWith(200, (builder) => {
builder.jsonBody({
orderId: "order-123",
status: "CONFIRMED",
totalAmount: 5000,
});
})
.executeTest(async (mockserver) => {
const res = await fetch(`${mockserver.url}/orders/order-123`);
const order = await res.json();
expect(order.orderId).toBe("order-123");
expect(order.status).toBe("CONFIRMED");
});
});
});
buf breaking --against .git#branch=mainで.protoの破壊的変更も検出できます。テスト戦略全般はClaude Codeテスト完全ガイドをご覧ください。
よくある質問
buf breakingで検出できます。外部に公開するAPIはREST + OpenAPIが適しています。両方を併用する「二刀流」がマイクロサービスの標準パターンです。--add-dirで他サービスへのアクセスを追加します。例: cd apps/order-service && claude --add-dir ../payment-service --add-dir ../../packages/proto。CLAUDE.mdには作業スコープを明記して、他サービスへの意図しない変更を防ぎましょう。まとめ
- サービス分割: Plan ModeでDDD分析→Bounded Context特定→ストラングラーフィグで段階的分割
- Connect RPC: .protoファイルから型安全なTypeScriptクライアント・サーバーを自動生成(Connect-ES 2.0 + buf)
- モノレポ構成: Turborepo + packages/protoで共有型管理。CLAUDE.md階層設計でサービス単位のスコープを明示
- Docker Compose:
depends_on: condition: service_healthyでサービス起動順序を保証 - 契約テスト: Pact V4で消費者駆動のコントラクト検証。
buf breakingで.proto互換性チェック
モノレポの詳細はClaude Code × モノレポ完全ガイド、API開発はClaude Code × API開発自動化ガイド、IaCはClaude Code × Terraform完全ガイドもあわせてご覧ください。

