doilux’s tech blog

ITに関する備忘録。 DDP : http://doiluxng.hatenablog.com/entry/2018/01/01/195409

競合イベントは同じテーブルパターン

例えばorder(申込)エンティティが以下の状態遷移をするとします。

f:id:doiluxng:20180101230936p:plain

※取消(cancel)、完了(complete)のどちらかを持ってライフサイクルが終わります

この時、状態遷移のイベントを残す以下のようなテーブル設計にすると、取消、完了の両方のイベントを記録することができてしまいます。

f:id:doiluxng:20180101231614p:plain

※PK、FKは主キー、外部キーを表します

取消と完了はどちらか一方のイベントしか許さないので一つのテーブルに記録すると、RDBMSの制約機能により、競合するイベントの登録がブロックされます。

f:id:doiluxng:20180101231856p:plain

※CHECKは検査制約を表します

イベント固有の情報があるとき

一方のイベント固有のイベントがある場合、カラムを追加するとNULLを許可する必要があるため、以下のように固有情報用のテーブルを作る方法があります。

f:id:doiluxng:20180101232738p:plain

※FOREIGN KEY(id, order_type)は複合外部キーを表す

検査制約によってorder_cancel_infoのorder_typeは'cancel'しか入らないため、取消時のみ格納できるテーブルになります。よって、「完了イベントなのに取消理由がある」という不整合からカードできます。ただし、「取消のイベントは記録されているのに、取消の情報がない」という不整合を許してしまう課題は残ります(相互参照にしてOracleの制約の遅延評価を使えばもしかして…)

アプリケーションでガードすべきか?

そもそもテーブル設計を頑張らなくてもアプリケーションでガードすればいいという考え方もあると思います。しかし、個人的には処理の一貫性(取消状態から完了に遷移しないなど、正しい状態遷移をする)はアプリケーションで担保、データの一貫性(前述の通り、取消イベントと完了イベントの両方があるなど、正しいデータの状態を保つ)はテーブル設計など、物理データモデルで担保するべきだと思います。