業務システムのテーブル設計、特にマスタデータ管理の設計についてまとめます。
- 追加、更新、削除
- 履歴整理
- 削除含む、過去の版の復元(アンドゥ)
- ブランチ、マージの概念
これらの振る舞いを実現するためのテーブル構造です。
1. 物理スキーマ:事実を「仕訳」と「残高」に分ける
本アーキテクチャでは、マスタデータを「過去の出来事」と「現在の正解」の2層に分離します。
物理構造としては伝統的な SCD Type 4(Slowly Changing Dimension Type 4) に分類されるものです。
イベントソーシングとCQRSの視点で理解すると良いかもしれません。
履歴テーブル(History / Event Store):仕訳帳
業務で発生したすべての出来事を insert し続ける、不変(Immutable)な記録です。
複式簿記における 「仕訳帳」 に相当します。
ここでは「何が起きたか(動詞)」が主役です。
create table master_room_history (
room_id varchar(16) not null,
revision int not null, -- 版
updater varchar(15) not null,
update_time datetime not null,
event_verb varchar(10) not null, -- 作用した「意図」の種類
parent_rev int not null, -- 直前の版
merge_parent int not null, -- コピー元の版
branch_id int not null, -- ブランチ識別子(0: Main, others: Plan)
room_name varchar(100) not null, -- 以降は属性(データ項目)
...
...
primary key (room_id, revision)
);
投影テーブル(Projection / Base Table):総勘定元帳
アプリケーションが参照する最新の情報です。通常の「マスタ」です。
本設計では履歴を積み上げた結果としての「現在の正解」を保持するキャッシュです。
複式簿記における 「元帳(残高)」 に相当します。
create table master_room (
room_id varchar(16) not null,
revision int not null, -- 版
updater varchar(15) not null,
update_time datetime not null,
room_name varchar(100) not null, -- 以降は属性(データ項目)
...
...
primary key (room_id)
);
2. 課題(痛み):上書きされ消える「意図」
以前の業務システムでは、マスタテーブルは update 文によって現在の状態を管理してきました。
しかし、障害発生時に重要なのは「なぜそうなったのか」という業務上の意図です。
従来は、この「意図」を探るため、保守担当(開発者)がプログラム(コード)の処理を追いかけます。
そして、ログを調査し、経緯を説明する作業に追われていました。
3. 導入効果
1. 履歴のつながりによる根拠の保証(トレーサビリティ)
単なる変更記録ではなく、すべてのデータが 「直前の版(parent_rev)」 と 「コピー元の版(merge_parent)」 の情報を持ちます。
- 「なぜこの値なのか」という問いに対し、推測に頼らずデータそのものを辿ることで客観的な裏付けが得られます
- 障害対応や監査において、ログをかき集めるといった「負担」を減らし、データのつながりを追うだけで、経緯にたどりつけます
2. 過去を書き換えないデータの再利用
過去のデータを修正するのではなく、
ある時点のデータを「最新の事実」として末尾に付け加えます(追記型)。
- 「削除からの復旧」や「過去への回帰」を、「新しい記録の追加」として扱います
- データベースを直接操作して無理やりデータを修正しません
- すべての修正が「記録」として残すので、経緯の連続性が途切れません
3. 「複数人の更新」「削除済み」の状態に対する衝突防止(楽観的ロック)
登録や変更では、版(revision)を使って、衝突を検出します。
マスタデータが「削除された状態」であっても、履歴に残っている版(revision)を使って、他者の操作とぶつからないように制御します。
- データが存在する期間だけでなく、削除されても「版(バージョン)」として管理に含めます
- 「誰かが消したことを知らないユーザー」による再登録や、古いデータに基づいた復元といった事故を防げます。複数人が同時に操作しても、データの正しさが保たれます
4. 準備作業の分離と一括反映(Branch & Merge)
現在動いているデータに影響を与えず、履歴テーブルの中の「隔離された場所」で、将来の計画データを作成します。
- 「今の値」と「将来の予定」を、一つの仕組みの中で共存させます
- 日中に落ち着いて作った計画を、任意のタイミングで「統合(マージ)」という操作で本番へ反映できます
3. 主役は、7つの動詞による「意図の仕訳」
動詞を主役にするとは、業務システムにおいて、データという「状態」を記録するということは、なにかしらの「意図」があるという世界観です。
現実に「意図」は様々ありますが、ここではを7種類に抽象化して分類します。
一般的な「追加・更新・削除」
動詞 (event_verb) |
作用 | 業務上の意図(主目的) |
|---|---|---|
init |
新規追加 | マスタを誕生させる。 |
revise |
更新 | 変化に合わせ、属性を調整する。 |
terminate |
削除 | 役割を終えたマスタを過去とする。 |
特殊操作
動詞 (event_verb) |
作用 | 業務上の意図(主目的) |
|---|---|---|
revert |
回帰 | 過去の履歴の復元。(削除データのUndo含む) |
branch |
枝の分岐 | 本番データを維持しつつ、仮説提案(計画案)を作る。 |
merge |
統合 | 計画案を確定させる。(提案の承認) |
baseline |
起点の再定義 | 複雑化した系譜を整理し、整地する。 |
4. 振る舞いの構造論:Git由来の「先行事実への参照」
データの連続性を担保する手法はGitを参考にします。
つまり、Gitがコミットグラフを形成する際に用いる 「先行事実への参照(Parent Reference)」 という手法です。
-
parent_rev(直接の先祖)- これは「どのリビジョンを土台にして、この変更を決断したか」という 直前の版 です
-
merge_parent(由来の先祖)- これは「真の出典元はどこか」という コピー元の版 です
各レコードが「どの過去を指し示しているか」という情報を保持し、原点から現在に至るまでの 「系譜」 を表現します。
5. 結論:計算された「今」と、消えない「系譜」
この「意図の集積」モデルを採用することで、マスタテーブルは以下の機能を獲得します。
-
完全な再現性
投影テーブル(元帳)に不備があっても、不変の履歴(仕訳帳)を最初から辿ることで、検証できます。 -
因果の透明性
「誰がどのような操作を経て今の状態に至ったか」という 動詞の連鎖 を証明できます。 -
並行世界の表現
「未承認の計画(Branch)」と「確定した現実(Main)」を、同一のテーブル構造で扱い、最終的な合流(Merge)に向けて共存できます。
つまり、「意図の集積」モデルを採用したマスタテーブルは
- 人々の「意図」が複式簿記的に積み重なり
- Gitのように分岐と統合を繰り返しながら成長していく
「事実の積み重ね」 を記述するための仕組みになります。
6. 実装は別記事にて
まずは「追加・更新・削除」+「復元(削除データのUndo)」の実装例を記事にしました。
(説明を簡略化するためSQLで記述していますが、用途に合わせて変換してください)