プロフィール DavidHLPL · RHINE LAB RSS ↗
資料 X-1105 / バックエンド工学

マイクロサービスデータ所有権:領域 owner を先に決めてから、DB 分割と移行を考える

技術ノート

公開 / DavidHLPL

結論優先:マイクロサービス分割の最初のステップは、データベースを複数台に分けることではなく、bounded context、最終書き込み owner、データアクセス契約を決めることだ。サービスデータは API/メッセージ経由でアクセスする。共有物理データベースサーバーは移行期のデプロイとして許容できるが、複数サービスが同じ業務 schema/テーブルを読み書きするのが主な結合だ。移行は expand → migrate → contract の順に進め、contract は旧オブジェクトの削除前に rollback window を閉じなければならない。

1. 四層の順序で owner を判定する

ビジネス能力 / bounded context
  -> owner service
  -> private schema/table
  -> API/event contract
  -> consumer と移行ゲート

「今だれがこのテーブルを直接参照しているか」から owner を逆算してはならない。それはたいてい歴史的な結合を表すだけだ。一つの領域には最終書き込み owner が一つあるべきだ。他のサービスの複製テーブル、read model、cache は派生データだと明示し、リフレッシュ・無効化・再構築の方法を定義しなければならない。

2. database-per-service の境界

database-per-service の核心は、永続化データがサービスに属し、他のサービスがサービス API/メッセージを迂回して業務テーブルへ直接アクセスしてはいけない、ということだ。データベースはサービス実装の一部なので、schema と migration も owner の変更責任に含めるべきだ。

見落とされがちな区別がある:

形態 自動的に共有データ境界になるか
複数サービスが同じ物理データベースサーバーを共有 ならない。移行期として schema/database を分離できる
複数サービスが同じ業務 schema/テーブルを読み書き なる。開発期と実行期の結合を生む
モジュラーモノリスのモジュール私有テーブル 必ずしも DB 分割は不要。まずモジュール境界を保つ

共有 schema の変更はすべてのアクセス側の調整を要し、クエリやロックは実行時にも互いに影響する。だから「複数プロセスに分割済み」は「データ自律が完了した」ことを意味しない。

3. クロスサービス一貫性:saga と outbox はそれぞれ一つの課題を解く

Saga

複数サービスや複数リソースにまたがるフローを複数のローカルトランザクションに分け、イベント/コマンドで進め、失敗時には補償トランザクションを実行する。Saga は中間状態の可視性、補償の冪等性、失敗の再試行可能性を要求する。ローカルトランザクションを透明な 2PC に変えるものではない。

本当に複数サービス・複数リソースにまたがり、境界を引き直せない場合にだけ saga を導入する。単一サービス内の更新が「将来分割するかもしれない」からといって、前倒しで分散トランザクションにしてはいけない。

Transactional outbox

業務状態と outbox レコードを同じローカルトランザクションで書き込む。コミットすれば両方が存在し、ロールバックすれば両方が消える。relay がイベント公開を担う。これは業務更新とメッセージ記録の間の原子性の断絶を解決する。

Outbox でも consumer の冪等性、重複配信処理、順序ポリシー、監視が必要だ。イベント駆動の公開経路がないなら導入する必要はない。

4. 段階的移行の三つのフェーズ

Expand

テーブル、フィールド、API を追加し、旧リード/ライトが動き続けるようにする。まず互換面を作り、最初のステップで旧カラムを削除したり唯一の writer を切り替えたりしない。

Migrate

ダブルライトまたは CDC での複製を使い、新旧データとイベント結果を検証しつつ、読み取り経路を徐々に新 owner へ切り替える。ダブルライトの失敗、遅延、リプレイには観測と補償が必要だ。

Contract

新システムが system of record になった後、旧オブジェクトと互換ロジックを削除する。削除は rollback window の閉鎖を意味するので、まず検証、バックアップ、アラート、代替ロールバック案を確認する。リクエストを途中で受けて新旧システムへ振り分けられない、旧システムを変更できない、検証可能な同期経路がない場合は、strangler fig を機械的に適用すべきではない。

最小領域監査

分割や移行の前に、領域ごとに記録する:

  1. bounded context と業務 owner;
  2. 現在の system of record と最終 writer;
  3. 同じテーブルを直接読み書きするすべてのサービス、スクリプト、レポート;
  4. API、イベント、outbox がすべての変更経路をカバーしているか;
  5. saga が本当に必要か、補償は冪等か;
  6. expand/migrate/contract の検証ゲート;
  7. contract 後のバックアップとロールバック代替;
  8. 共有物理インスタンスと共有 schema の違い。

このチェックリストは判定基準であって、特定プロジェクトの事実ではない。プロジェクト側は固定コミット内の schema、クエリ、設定、呼び出し元を読み続けなければならない。

よくある誤解

  • 「一サービス一データベースサーバーでなければ所有権ではない。」 所有権はまずアクセス境界と最終 writer で定義される。物理的な分離は段階的に完了できる。
  • 「共有テーブルは開発の便利さにすぎない。」 それは同時に schema 変更の調整と実行時のロック/クエリ結合を生む。
  • 「Saga は分散トランザクションの痛みのない代替だ。」 補償、中間状態、冪等性の要求を追加する。
  • 「outbox を足せばメッセージが一度だけ送られる。」 outbox はレコードとローカルトランザクションの一致を保証するが、重複配信をなくすものではない。
  • 「contract は最後に旧コードを削除するだけだ。」 それは rollback window を閉じることであり、独立した移行ゲートとして扱わなければならない。

証拠の境界

revision-pinned ドキュメントと live ページの階層はリポジトリ src/content/raw/zh-cn/microservice-domain-data-ownership-correction.md にある。初期スナップショットも訂正スナップショットも、現在のプロジェクトが実際にどのサービス、テーブル、owner を持つかを証明しない。実際の帰属には対象リポジトリのソースと schema の証拠が必要だ。

出典と関連文書

研究記録

  1. 元の文書: src/content/note/ja/microservice-data-ownership.md
  2. 出典リビジョン: f49953fb0d4bd52a39bf81167b0c4b7dd7d78a64
  3. 証拠の状態: 有効

原文の出典 ↗