0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

マイクロサービス ⇔ モノリシック瞬時切り替え可能なアーキテクチャ設計

Last updated at Posted at 2025-07-18

はじめに

スタートアップでサービスを立ち上げる際、初期コスト削減将来的なスケーラビリティの両立は常に課題です。本記事では、開発環境ではマイクロサービス本番環境ではモノリシックで運用し、収益化に応じて瞬時にマイクロサービスに切り替え可能なアーキテクチャをご紹介します。

🎯 設計思想とメリット

なぜこのアーキテクチャを選んだのか

  1. 初期コスト削減

    • クラウド環境で20個以上のpodを動かすとコストが高い
    • 小規模サービスではマイクロサービスのメリットが薄い
  2. データ不整合リスク回避

    • モノリシック→マイクロサービス移行時の分散データベース問題
    • 事前にサービス境界を明確にしておくことで移行リスクを最小化
  3. 開発効率とパフォーマンスの両立

    • 開発時はマイクロサービスで責任分離とテスト効率を向上
    • 本番時はモノリシックで通信オーバーヘッドを削減

🏗️ アーキテクチャ概要

📁 OmniDesk/
├── 📁 services/
│   ├── 📁 api/
│   │   ├── 📁 development/    # マイクロサービス用
│   │   │   ├── 📁 account/
│   │   │   │   ├── 📁 user/
│   │   │   │   ├── 📁 plan/
│   │   │   │   └── 📁 payment/
│   │   │   ├── 📁 inbox/
│   │   │   └── 📁 input/
│   │   └── 📁 product/        # モノリシック用
│   │       ├── 📁 controllers/
│   │       ├── 📁 models/
│   │       ├── 📁 routes/
│   │       └── 📁 migrations/
│   ├── 📁 ui/                 # React + TypeScript
│   ├── 📁 integration/        # 外部連携
│   └── 📁 batch/             # バッチ処理
├── 📁 k8s/                   # Kubernetes設定
├── 📁 compose/               # Docker Compose設定
├── 📁 infra/                 # インフラ構成
└── 📁 scripts/reflect/       # 自動変換スクリプト

🔧 マイクロサービス構成(開発環境)

1. サービス分割戦略

各サービスは独立したDockerコンテナとして動作し、明確な責任境界を持ちます。

アカウント系サービス例

// services/api/development/account/user/routes/routes.go
package routes

import (
    "github.com/gin-gonic/gin"
    "OmniDesk-user-backend/controllers"
    middleware "promochain.com/external/module/middleware"
)

func SetupRoutes(router *gin.Engine, db *gorm.DB, rabbitManager *middleware.RabbitMQManager) {
    userRoutes := router.Group("/api/users")
    {
        userRoutes.GET("/:id", controllers.GetUser)
        userRoutes.PUT("/:id", controllers.UpdateUser)
        userRoutes.POST("/register", controllers.RegisterUser)
        userRoutes.POST("/login", controllers.LoginUser)
        userRoutes.PUT("/update-language", controllers.UpdateLanguageCode)
    }

    // RabbitMQ設定
    amqpURL := fmt.Sprintf("amqp://%s:%s@%s:%s/", 
        os.Getenv("RABBIT_MQ_USER"), 
        os.Getenv("RABBIT_MQ_PASSWORD"), 
        os.Getenv("RABBIT_MQ_HOST"), 
        os.Getenv("RABBIT_MQ_AMQP_PORT"))
    
    rabbitManager, err := middleware.NewRabbitMQManager(amqpURL, os.Getenv("SERVER_PERFORMANCE_QUEUS"))
    if err != nil {
        log.Fatalf("RabbitMQManager の初期化に失敗: %v", err)
    }
}

2. Kubernetes構成(20+pods)

# k8s/step1-infra/deployments/infra-redis-cluster.yml
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: redis-cluster
  namespace: default
spec:
  serviceName: redis-cluster
  replicas: 6
  template:
    spec:
      containers:
      - name: redis
        image: infra-cache-redis:latest
        ports:
        - containerPort: 6379
        command:
        - redis-server
        args:
        - --cluster-enabled yes
        - --maxmemory 256mb
        - --maxmemory-policy allkeys-lru

インフラサービス構成

  • Redis Cluster: 6 pods(キャッシュ戦略)
  • RabbitMQ: 3 pods(メッセージキュー)
  • Kafka: 3 pods(イベントストリーミング)
  • Prometheus: 1 pod(メトリクス収集)
  • Grafana: 1 pod(モニタリング)
  • 各マイクロサービス: 6+ pods
  • MySQL: 各サービス専用DB

🚀 モノリシック構成(本番環境)

1. Docker Compose設定

# compose/docker-compose.prod.yml
services:
  mysql:
    environment:
      - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}
    deploy:
      resources:
        limits:
          cpus: '2'
          memory: 2G
        reservations:
          cpus: '1'
          memory: 1G

  backend:
    env_file:
      - .env
    ports:
      - ${REACT_APP_PRODUCTION_PORT}:${REACT_APP_PRODUCTION_PORT}
    deploy:
      resources:
        limits:
          cpus: '1'
          memory: 1G
        reservations:
          cpus: '0.5'
          memory: 512M

  ui:
    deploy:
      resources:
        limits:
          cpus: '0.5'
          memory: 512M

  nginx:
    ports:
      - "80:80"
      - "443:443"

2. 統合されたルート構成

// services/api/product/routes/routes.go
package routes

func SetupRoutes(router *gin.Engine, db *gorm.DB, rabbitManager *middleware.RabbitMQManager) {
    // 全マイクロサービスのコントローラーを統合
    PaymentController := &controllers.PaymentController{DB: db}
    
    // Route Group定義(元の境界を維持)
    userRoutes := router.Group("/api/users")
    planRoutes := router.Group("/api/plan")
    billingRoutes := router.Group("/api/billing")
    inboxRoutes := router.Group("/api")
    
    // 統合されたエンドポイント
    userRoutes.GET("/:id", controllers.GetUser)
    userRoutes.POST("/register", controllers.RegisterUser)
    planRoutes.GET("", controllers.GetUserPlan)
    planRoutes.POST("/register/:plan_token", controllers.RebaseNewPlan)
    billingRoutes.GET("", controllers.GetBillingInfo)
}

⚡ 自動変換システム

1. ルート統合スクリプト

#!/bin/bash
# scripts/reflect/merge_routes_dynamic.sh

echo "=== マイクロサービス→モノリシック ルート統合 ==="

SERVICE_DEVELOPMENT_DIR="$PROJECT_ROOT/services/api/development"
SERVICE_PRODUCT_DIR="$PROJECT_ROOT/services/api/product"
TARGET_FILE="$SERVICE_PRODUCT_DIR/routes/routes.go"

# 各マイクロサービスのroutes.goを検索
ROUTE_FILES=$(find "$SERVICE_DEVELOPMENT_DIR" -name "routes.go" -type f)

# 構造別に抽出
for file in $ROUTE_FILES; do
    # 1. import文の抽出
    awk '/^import \(/,/^\)/' "$file" | grep -E '^\s*[a-zA-Z_]*\s*"[^"]*"'
    
    # 2. Controller定義の抽出
    awk '/Controller.*:=.*&.*{/,/^[[:space:]]*}$/' "$file"
    
    # 3. Route Group定義の抽出
    grep -E 'router\.Group\(' "$file"
    
    # 4. HTTPメソッド定義の抽出
    grep -E '\.(GET|POST|PUT|DELETE|PATCH)\(' "$file"
done

# 統合されたroutes.goを生成
cat > "$TARGET_FILE" << 'EOF'
package routes

import (
    // 統合されたimport文
)

func SetupRoutes(router *gin.Engine, db *gorm.DB, rabbitManager *middleware.RabbitMQManager) {
    // 統合されたルート定義
}
EOF

2. Makefile環境切り替え

# Makefile抜粋
.PHONY: k8s-development-deploy product-build

# 開発環境(Kubernetes + マイクロサービス)
k8s-development-deploy-step1:
	./scripts/development/deploy/kubernetes-deploy-step1-infra.sh
k8s-development-deploy-step2:
	./scripts/development/deploy/kubernetes-deploy-step2-mysql.sh
k8s-development-deploy-step3:
	./scripts/development/deploy/kubernetes-deploy-step3-backend.sh

# 本番環境(Docker Compose + モノリシック)
product-build:
	./scripts/product/run-build.sh
	./scripts/reflect/merge_routes_dynamic.sh
	./scripts/reflect/merge_migrations_dynamic.sh

📊 パフォーマンス最適化戦略

1. キャッシュ戦略(Redis)

# Redis Cluster設定
args:
- --maxmemory 256mb
- --maxmemory-policy allkeys-lru
- --save "900 1"  # 15分に1回保存
- --appendfsync everysec

2. モニタリング(Prometheus + Grafana)

# k8s/step1-infra/deployments/infra-prometheus.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: prometheus
spec:
  template:
    spec:
      containers:
      - name: prometheus
        image: infra-monitoring-prometheus:latest
        ports:
        - containerPort: 9090
        volumeMounts:
        - name: prometheus-config
          mountPath: /etc/prometheus

3. メッセージキュー(RabbitMQ + Kafka)

  • RabbitMQ: リアルタイム処理、パフォーマンスログ
  • Kafka: イベントソーシング、大容量データストリーミング

💰 コスト比較

開発環境(Kubernetes)

  • pods数: 20+
  • AWS EKS想定コスト: ~$300-500/月
  • 開発効率: ⭐⭐⭐⭐⭐

本番環境(Docker Compose)

  • containers数: 5
  • AWS EC2想定コスト: ~$50-100/月
  • 運用コスト: ⭐⭐⭐⭐⭐

🔄 切り替えタイミング戦略

Phase 1: 収益 < $1000/月

  • モノリシックで運用
  • コスト最小化重視

Phase 2: 収益 $1000-5000/月

  • パフォーマンスボトルネック監視
  • 段階的マイクロサービス移行検討

Phase 3: 収益 > $5000/月

  • フルマイクロサービス移行
  • オートスケーリング導入

🛠️ 実装のポイント

1. サービス境界の明確化

// 各サービスで独立したDB接続
type UserService struct {
    DB *gorm.DB
    Redis *redis.Client
    RabbitMQ *rabbitmq.Connection
}

// 明確なAPI契約
type UserAPI interface {
    GetUser(id string) (*User, error)
    CreateUser(user *User) error
    UpdateUser(id string, user *User) error
}

2. 環境変数による設定切り替え

# .env
ENVIRONMENT=development  # or production
REDIS_MODE=cluster       # or single
DATABASE_MODE=distributed # or monolithic

3. CI/CDパイプライン

# .github/workflows/deploy.yml
jobs:
  deploy-development:
    if: github.ref == 'refs/heads/develop'
    steps:
      - name: Deploy to Kubernetes
        run: make k8s-development-deploy
        
  deploy-production:
    if: github.ref == 'refs/heads/main'
    steps:
      - name: Build Monolithic
        run: make product-build
      - name: Deploy to Docker Compose
        run: docker-compose -f compose/docker-compose.prod.yml up -d

📈 監視・アラート設定

Grafanaダッシュボード例

  • レスポンス時間: 95パーセンタイル監視
  • エラー率: 5%超過でアラート
  • リソース使用率: CPU/メモリ使用率
  • データベースパフォーマンス: スロークエリ監視

🎉 まとめ

このアーキテクチャにより:

初期コストを80%削減($500→$100/月)
開発効率を維持(マイクロサービスの利点)
スケーラビリティを確保(瞬時切り替え可能)
データ不整合リスクを最小化(事前境界設計)

スタートアップの成長フェーズに応じて柔軟にアーキテクチャを変更できる、実用的なソリューションです。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?