Chuyển đến nội dung chính

レッスン 8: サービスごとのデータベースと多言語の永続性

サービスごとのデータベースの原則、データベースを共有しない理由、ポリグロット永続性 (サービスごとに適切な DB を選択する)、データ所有権、およびサービス間のデータ クエリ戦略。

🏗️ アーキテクチャ — レッスン 8 レッスン 8: サービスごとのデータベースと多言語対応 持続性

クラウドネイティブのマイクロサービスアーキテクチャ

パート 3: マイクロサービスにおけるデータ管理

xdev.asia

レッスン 8: サービスごとのデータベースと多言語の永続性

はじめに

モノリスでは、すべてのモジュールが 1 つのデータベースを共有します。マイクロサービスでは、各サービスは 独自のデータベースを所有します。この原則は疎結合を実現するための基礎ですが、同時に多くの新しいデータ整合性の課題も生み出します。


1. サービスパターンごとのデータベース

1.1 原則

✅ Database per Service:
┌────────────┐    ┌────────────┐    ┌────────────┐
│   Order    │    │  Payment   │    │  Catalog   │
│  Service   │    │  Service   │    │  Service   │
└─────┬──────┘    └─────┬──────┘    └─────┬──────┘
      │                 │                 │
┌─────▼──────┐    ┌─────▼──────┐    ┌─────▼──────┐
│ PostgreSQL │    │ PostgreSQL │    │  MongoDB   │
│  (orders)  │    │ (payments) │    │ (products) │
└────────────┘    └────────────┘    └────────────┘

Quy tắc: KHÔNG truy cập DB của service khác trực tiếp.
Muốn data từ Payment? → Gọi Payment API.

1.2 データベースを共有しないのはなぜですか?

❌ Shared Database:
┌────────────┐    ┌────────────┐
│   Order    │    │  Payment   │
│  Service   │    │  Service   │
└─────┬──────┘    └─────┬──────┘
      │                 │
      └────────┬────────┘
         ┌─────▼──────┐
         │ Shared DB  │
         │ (all tables)│
         └────────────┘

Vấn đề:
├── Schema coupling: Payment đổi schema → Order bị broken
├── Performance coupling: Query nặng từ Order → Payment bị chậm
├── Deployment coupling: DB migration phải coordinate cả 2 team
├── Scaling coupling: Không thể scale DB riêng cho từng service
└── Technology coupling: Tất cả phải dùng cùng DB engine

1.3 分離戦略

Strategy 1: Separate Database (khuyến nghị)
├── Mỗi service một database instance
├── Cách ly hoàn toàn
└── Chi phí cao hơn nhưng an toàn nhất

Strategy 2: Separate Schema
├── Cùng database instance, khác schema
├── Cách ly ở mức schema
└── Chi phí thấp hơn, phù hợp start

Strategy 3: Separate Tables
├── Cùng schema, khác tables
├── Cách ly yếu nhất
└── Chỉ phù hợp giai đoạn đầu migration

2. 多言語の永続性

2.1 適切なデータベースの選択

各サービスは、そのデータ特性に最適なデータベースを選択します。

サービスデータベース理由
注文ポストグレSQLACID トランザクション、リレーショナル データ、複雑なクエリ
製品カタログモンゴDB柔軟なスキーマ、ネストされたドキュメント、さまざまな製品タイプ
ユーザーセッションレディスインメモリ、サブミリ秒アクセス、自動期限切れ (TTL)
検索エラスティックサーチ全文検索、転置インデックス、ファセット検索
アクティビティフィードアパッチカサンドラ高い書き込みスループット、時系列、分散
推薦ネオ4jグラフの関係 (「X を購入したユーザーは Y も購入しました」)
ショッピングカートRedis/DynamoDBキーと値、高速アクセス、一時的なデータ
分析クリックハウス列指向、OLAP、集計クエリ
ファイル/画像S3/MinIOオブジェクト ストレージ、無制限のスケール

2.2 実践例: 電子商取引

┌──────────┐  PostgreSQL   ┌──────────┐  MongoDB
│  Order   │──────────────▶│ Catalog  │──────────▶
│  Service │  (orders,     │ Service  │  (products,
└──────────┘   line_items) └──────────┘   variants)

┌──────────┐  Redis        ┌──────────┐  Elasticsearch
│  Cart    │──────────────▶│  Search  │──────────▶
│  Service │  (cart:{uid})  │ Service  │  (products index)
└──────────┘               └──────────┘

┌──────────┐  PostgreSQL   ┌──────────┐  ClickHouse
│ Payment  │──────────────▶│Analytics │──────────▶
│ Service  │  (payments,   │ Service  │  (events,
└──────────┘   refunds)    └──────────┘   aggregates)

3. サービス間のデータクエリ

3.1 問題

顧客情報や製品情報などの注文の詳細を表示する必要がある場合:

❌ Trước (monolith): 
  SELECT o.*, c.name, p.title 
  FROM orders o
  JOIN customers c ON o.customer_id = c.id
  JOIN products p ON oi.product_id = p.id

✅ Sau (microservices):
  Order, Customer, Product ở databases khác nhau → Không thể JOIN!

3.2 解決策: API の構成

API Gateway / BFF / Composite Service:

1. GET /orders/O-001     → Order Service    → {order_id, customer_id, items}
2. GET /customers/C-042  → Customer Service → {name, email}
3. GET /products/P-100   → Product Service  → {title, image}

4. Compose response:
{
  "order": {
    "id": "O-001",
    "customer": {"name": "Nguyen Van A", "email": "[email protected]"},
    "items": [
      {"product": {"title": "iPhone 16", "image": "..."}, "quantity": 1}
    ]
  }
}

3.3 ソリューション: CQRS + マテリアライズド ビュー

Tạo read-optimized view bằng cách subscribe events:

Order.Created ──▶ ┌─────────────────────┐
Customer.Updated ──▶│  Order Detail View  │
Product.Updated  ──▶│  (Elasticsearch)    │
                   │                     │
                   │  {order + customer  │
                   │   + product details}│
                   └─────────────────────┘

Query: GET /order-details/O-001 → Trả kết quả đã composed sẵn

3.4 戦略の比較

戦略長所短所使用例
API構成シンプルなリアルタイム データ遅延 (複数回の呼び出し)、部分的な障害ダッシュボード、管理 UI
CQRS + マテリアライズド ビュー高速読み取り、事前構成結果整合性、複雑さ顧客対応、検索
データ レプリケーション (イベント)高速なローカル クエリ古いデータ、ストレージの重複読み取り負荷の高いサービス

4. データの所有権

4.1 ルール

Mỗi piece of data có MỘT owner duy nhất:

Customer data → Customer Service (owner)
  ├── Order Service: giữ customer_id (reference)
  ├── Payment Service: giữ customer_id (reference)
  └── Notification Service: subscribe CustomerUpdated event

Price data → Catalog Service (owner)
  └── Order Service: snapshot giá tại thời điểm order
       (không query lại, vì giá có thể thay đổi)

4.2 データスナップショットパターン

Khi tạo Order, snapshot data cần thiết:

Order {
  id: "O-001",
  customer_snapshot: {        ← Copy tại thời điểm order
    name: "Nguyen Van A",
    address: "123 ABC"
  },
  items: [{
    product_id: "P-100",
    title_snapshot: "iPhone",  ← Copy tại thời điểm order
    price_snapshot: 25000000   ← Giá tại thời điểm order
  }]
}

→ Customer đổi address sau đó? Order vẫn giữ address cũ (đúng)
→ Product tăng giá? Order vẫn giữ giá cũ (đúng)

5. データベース移行戦略

5.1 共有DBからサービスごとのデータベースへ

Phase 1: Identify boundaries
  Shared DB → Xác định tables thuộc service nào

Phase 2: Create APIs
  Service A gọi Service B qua API thay vì JOIN

Phase 3: Sync data
  Dual-write hoặc CDC để sync trong quá trình migration

Phase 4: Split databases
  Move tables sang database riêng

Phase 5: Remove old connections
  Xoá direct DB access, chỉ giữ API calls

6. まとめ

コンセプトキーポイント
サービスごとのデータベース各サービスは共有ではなく独自のデータベースを所有します。
多言語の永続性各サービスに適切な DB を選択する
API構成集約 API 呼び出しによるクロスサービス クエリ
CQRS + 表示複雑なクエリに対して読み取りに最適化されたビューを作成する
データの所有権各データには一意のサービス所有者がいます。
データのスナップショット取引時に必要なデータをコピー

次の記事: イベント ソーシングと CQRS — 状態をイベントとして保存し、読み取り/書き込みモデルを分離します。