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?

Hybrid License System Day 3: サービス分割の設計原則

Last updated at Posted at 2025-12-02

🎄 科学と神々株式会社 アドベントカレンダー 2025

Hybrid License System Day 3: サービス分割の設計原則

マイクロサービス基礎編 (2/4)


📖 はじめに

Day 3では、サービス分割の設計原則を学びます。ドメイン駆動設計(DDD)の基礎と、Bounded Contextの考え方を理解し、Auth/Admin/Gatewayという分割の設計思想を深く掘り下げます。


🎯 ドメイン駆動設計(DDD)の基礎

DDDとは?

**ドメイン駆動設計(Domain-Driven Design)**は、ビジネスドメインに焦点を当てたソフトウェア設計手法です。

ビジネスドメイン → コードに反映
     ↓
ドメインモデル(概念モデル)
     ↓
マイクロサービス境界

Bounded Context(境界づけられたコンテキスト)

Bounded Contextは、特定のドメインモデルが有効な境界を定義します。

┌─────────────────────────────────────────┐
│  License System (全体ドメイン)          │
│                                         │
│  ┌───────────────┐  ┌────────────────┐ │
│  │ Authentication│  │ Administration │ │
│  │   Context     │  │    Context     │ │
│  │               │  │                │ │
│  │ - User        │  │ - Dashboard    │ │
│  │ - License     │  │ - Statistics   │ │
│  │ - Token       │  │ - Audit        │ │
│  └───────────────┘  └────────────────┘ │
│                                         │
│  ┌───────────────────────────────────┐  │
│  │      Gateway Context              │  │
│  │  - Routing                        │  │
│  │  - Security                       │  │
│  │  - Rate Limiting                  │  │
│  └───────────────────────────────────┘  │
└─────────────────────────────────────────┘

🔍 Hybrid License Systemのサービス分割分析

1. Authentication Context → Auth Service

責務: ユーザー認証とライセンス管理

// Authentication Contextのドメインモデル
class User {
  userId: string;
  email: string;
  passwordHash: string;
  plan: string;
}

class License {
  licenseId: string;
  userId: string;
  clientId: string;
  status: string;
  expiresAt: Date;
}

class Token {
  jwt: string;
  payload: {
    userId: string;
    email: string;
    plan: string;
    clientId: string;
  };
}

分離理由:

  • セキュリティ: 認証処理は高度なセキュリティが必要
  • 独立性: ライセンス検証ロジックは他の機能と独立
  • スケーラビリティ: 認証リクエストが多いため独立スケール必要

2. Administration Context → Admin Service

責務: 管理機能と統計情報

// Administration Contextのドメインモデル
class Dashboard {
  totalUsers: number;
  activeLicenses: number;
  revenue: number;
  recentActivations: Activation[];
}

class AuditLog {
  logId: string;
  userId: string;
  action: string;
  timestamp: Date;
  ipAddress: string;
}

class Statistics {
  period: string;
  userGrowth: number[];
  licenseUsage: Map<string, number>;
  planDistribution: Map<string, number>;
}

分離理由:

  • ユーザー分離: 管理者と一般ユーザーで異なる機能
  • データアクセスパターン: 集計・分析クエリが多い
  • UI独立性: React SPAとして独立した進化が可能

3. Gateway Context → API Gateway

責務: クライアント向けの統一インターフェース

// Gateway Contextのドメインモデル
class Route {
  path: string;
  method: string;
  targetService: string;
  authRequired: boolean;
}

class RateLimit {
  ip: string;
  userId?: string;
  requestCount: number;
  windowStart: Date;
}

class SecurityPolicy {
  corsOrigins: string[];
  rateLimit: number;
  jwtSecret: string;
}

分離理由:

  • 横断的関心事: CORS、レート制限、認証など
  • 単一エントリーポイント: クライアントの簡素化
  • バックエンド隔離: 内部構成の変更をクライアントから隠蔽

📐 サービス境界の決め方

原則1: ビジネス能力で分割

各サービスは独立したビジネス価値を提供すべきです。

✅ 良い分割: 「ライセンス認証」サービス
   → ビジネス価値: ライセンスをアクティベートし、検証する

❌ 悪い分割: 「データベースアクセス」サービス
   → 技術的な分割であり、ビジネス価値が不明確

原則2: データの所有権

各サービスは自分のデータを所有すべきです。

Auth Service:
  - users テーブル (所有)
  - licenses テーブル (所有)
  - subscriptions テーブル (所有)

Admin Service:
  - audit_logs テーブル (所有)
  - statistics キャッシュ (所有)
  - ※ users/licenses は参照のみ(共有DB経由)

原則3: 変更の頻度

一緒に変更される機能は同じサービスに配置します。

Auth Service の変更例:
  ✅ JWT生成ロジック変更 → ライセンス検証ロジックも変更
  ✅ 新しいプラン追加 → アクティベーション処理も変更

Admin Service の変更例:
  ✅ ダッシュボードUI変更 → 統計API変更
  ✅ 新しいレポート追加 → データ集計ロジック変更

原則4: チームの境界

理想的には1チーム = 1サービスです。

Team Authentication:
  - Auth Service開発・運用
  - セキュリティ専門知識

Team Administration:
  - Admin Service開発・運用
  - フロントエンド/データ分析

Team Platform:
  - API Gateway開発・運用
  - インフラ/SRE

🤔 分離すべきか、統合すべきか?

ケーススタディ: User管理

選択肢1: 独立したUser Service

┌─────────────┐  ┌──────────────┐  ┌─────────────┐
│ User Service│  │ Auth Service │  │Admin Service│
│             │  │              │  │             │
│ - Users     │  │ - Licenses   │  │ - Dashboard │
│ - Profiles  │  │ - Tokens     │  │ - Audit     │
└─────────────┘  └──────────────┘  └─────────────┘

メリット:

  • User管理の独立した進化
  • 他サービスからの分離

デメリット:

  • サービス間通信の増加
  • トランザクション管理の複雑化

選択肢2: Auth Serviceに統合(Hybrid実装の選択)

┌──────────────────┐  ┌─────────────┐
│  Auth Service    │  │Admin Service│
│  - Users         │  │ - Dashboard │
│  - Licenses      │  │ - Audit     │
│  - Tokens        │  │ - Statistics│
└──────────────────┘  └─────────────┘

メリット:

  • トランザクション一貫性
  • パフォーマンス(ネットワーク不要)
  • シンプルな実装

デメリット:

  • Auth Serviceの責務増加

Hybrid実装の判断:

  • ユーザーとライセンスは密結合
  • トランザクション一貫性が重要
  • → Auth Serviceに統合

🔄 Context Mapping(コンテキスト間の関係)

Shared Database Pattern

Hybrid実装ではShared Databaseパターンを採用:

-- 共有データベース(SQLite)
CREATE TABLE users (...);        -- Auth Serviceが所有
CREATE TABLE licenses (...);     -- Auth Serviceが所有
CREATE TABLE subscriptions (...);-- Auth Serviceが所有
CREATE TABLE audit_logs (...);   -- Admin Serviceが所有

関係性:

  • Auth Service → Admin Service: Upstream/Downstream

    • Auth Serviceがデータを生成
    • Admin Serviceが統計用に参照
  • API Gateway → Auth/Admin: Customer/Supplier

    • Gatewayがクライアント要求を転送
    • Auth/Adminが実際の処理を提供

Anti-Corruption Layer

Admin ServiceはAnti-Corruption Layerを実装:

// Admin Serviceでの読み取り専用アクセス
class UserRepository {
  // Auth Serviceのデータモデルに依存しない
  async getUserStatistics() {
    // 内部的にusersテーブルを参照
    const stmt = db.prepare('SELECT plan, COUNT(*) as count FROM users GROUP BY plan');
    const rows = stmt.all();

    // Admin Service独自のモデルに変換
    return rows.map(row => ({
      planName: row.plan,
      userCount: row.count
    }));
  }
}

📊 サービス分割の評価基準

1. 凝集度(Cohesion)

サービス内の機能が密接に関連しているか?

Auth Service:
  ✅ ライセンスアクティベーション
  ✅ ライセンス検証
  ✅ JWT生成
  ✅ ユーザー認証

  → すべて「認証」という共通のビジネス能力

2. 結合度(Coupling)

サービス間の依存が少ないか?

API Gateway → Auth Service:
  ✅ HTTP REST API経由の疎結合
  ✅ サービス間認証(SERVICE_SECRET)
  ✅ タイムアウト・リトライ設定

Auth Service → Admin Service:
  ✅ 直接呼び出しなし(データベース共有のみ)

3. 自律性(Autonomy)

サービスが独立してデプロイできるか?

# Auth Serviceのみ更新
docker-compose up -d --build auth-service

# Admin Serviceは影響を受けない
curl http://localhost:3002/health
# → {"status": "healthy"}

🚀 実装における決定事項

Hybrid実装の設計決定

決定事項 選択 理由
User管理 Auth Serviceに統合 トランザクション一貫性
データベース Shared Database 実装の簡素化
サービス間通信 HTTP REST 標準的で実装が容易
認証方式 JWT ステートレスでスケーラブル
ゲートウェイ Express.js 柔軟性とエコシステム

🎯 次のステップ

Day 4では、サービス間通信パターンについて学びます。同期通信(HTTP/REST)、非同期通信、サービスディスカバリー、タイムアウト・リトライ戦略を実装しましょう。


🔗 関連リンク


次回予告: Day 4では、HTTP RESTによる同期通信と、タイムアウト・エラーハンドリングの実装を詳しく解説します!


Copyright © 2025 Gods & Golem, Inc. All rights reserved.

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?