StateにないときはEventを参照するパターン
いわゆるイベントドリブンな設計が好きだ。
CREATE TABLE ord_event( id NUMBER(10) PRIMARY KEY, payment_type VARCHAR(255) CHECK payment_type IN('full', 'installments'), ord_at DATE NOT NULL ); CREATE TABLE complete_event( id NUMBER(10) PRIMARY KEY REFERENCE ord_event(id), com_at DATE NOT NULL );
エンティティを取り出すときは以下のようなクエリを使う。
SELECT o.id , o.payment_type , o.ord_at , c.comp_at FROM ord_event o LEFT JOIN complete_event c ON o.id = c.id WHERE o.id = #{id} ;
ここでこんな要件が出てきたとします。
「お客様は契約開始までの間は支払い種別を変更できるようにしてー」
となるとイベントドリブンな設計に基づくとこんなテーブルができる。
CREATE TABLE pt_change_event( id NUMBER(10) PRIMARY KEY, ord_id NUMBER(10) NOT NULL REFERENCES ord_event(id), payment_type VARCHAR(255) CHECK payment_type IN('full', 'installments'), changed_at DATE );
参照する時のクエリはこうなるはず。
SELECT o.id , COALSCE(t.payment_type, o.payment_type) , o.ord_at , c.comp_at FROM ord_event o LEFT JOIN complete_event c ON o.id = c.id LEFT JOIN ( SELECT id , ord_id , payment_type , max(id) partition by(ord_id order by changed_at) as max_id FROM pt_change_event ) t ON t.id = t.max_id AND o.id = t.ord_id WHERE o.id = #{id} ;
実に複雑。なんで、ステートテーブルを作った方がいいと思う。 参照する時のクエリはこうなるはず。
SELECT COALSCE(s.id, o.id) , COALSCE(s.payment_type, o.payment_type) , COALSCE(s.ord_at, o.ord_at) , COALSCE(s.comp_at, c.comp_at) FROM ord_state s RIGHT JOIN ord_event o ON s.id = o.id LEFT JOIN complete_event c ON o.id = c.id WHERE o.id = #{id} ;
COALSCEを使えば「ステートテーブルになかったときはイベントを見に行く」という実装ができるので、システムを稼働してからステートテーブルを作った場合でもデータ移行なしでステートに移行できる。
まあ、もちろん、更新が走ったときはステートを更新しなきゃなので、upsert(あればupdateなければinsert)みたいなことはしなきゃだけど