時間経過とともに状態の変わるリソース
コンテンツサイトの「記事」を、あらかじめ掲載開始予定日を設定しておき、その日がきたら記事が公開される、また掲載終了予定日を設定しておいて、その日がきたら記事が非公開になる、というモデルを考える。
「記事」自体は日時属性を持たず、作成され公開され非公開になり消滅するライフサイクルが存在するので、これはリソースである。
記事は少なくとも掲載前のドラフトの段階と、掲載中の段階でコンテンツのValidな状態が異なる(コンテンツが公開に必要な形式を満たしていなくても保存できてほしいなど)ことが多いので、サブタイプとして表現する。
https://gyazo.com/61879a6ea778f4126b98706010d76a35
記事のライフサイクルで発生するイベントによって、記事の状態が変わる。
「掲載開始」のイベントによって、記事の掲載区分が「掲載中記事」になる。
「掲載終了」のイベントによって、記事の掲載区分が「掲載終了」になる。
というのが、イミュータブルデータモデルで考えたときのメンタルモデルになる。これを素直に実装すると、日付の切り替わりのタイミングで、
掲載開始予定日が到来したドラフト記事に対して、掲載開始イベントを生成し、掲載中記事に変異させる。
掲載終了予定日が到来した掲載中記事に対して、掲載終了イベントを生成し、掲載終了記事に変異させる。
というものになるだろう。(大抵はこれを夜間バッチとして扱う)
https://gyazo.com/28e25e48f18892be6f51ed7e01948bce
実装上の懸念は、記事が大量に存在すると、掲載開始のイベントや掲載終了のイベントの処理が遅れることである。これを回避するために、次の実装パターンが考えられる。
記事をシングルテーブル継承パターンで1つのテーブルに実装する
この場合、イベント発生して処理させるのではなく、記事を表示する側で単にSELECT時に現在日付を掲載開始日と掲載終了日で挟みうって該当するものだけを掲載中として表示することになる。
https://gyazo.com/b8f60035bfe2f4f94582da3ced4fe734
イベントを発生させないので、前述の実装と比べて以下の点でデメリットがある。
1. 「掲載開始」「掲載終了」のイベントが残らない。つまり記事の掲載開始日、掲載終了日が変更されたら、その記事はある時点で掲載状態だったのかどうか、は知りようがなくなる。
2. コンテンツサイトでの主要ユースケースであろう「掲載中の記事を検索する」などを実現するには、必ずWHERE句に「現在日時 BETWEEN 掲載開始日 AND 掲載終了日」を入れなくてなならない。
SQLの↑の条件のつけ忘れに伴う、掲載中でない記事が公開されてしまうバグを引き起こす可能性がある。
3. 工夫しだいで回避可能ではあるが、単純なシングルテーブル継承だと、コンテンツのValid状態の違いなどをアプリケーションレイヤだけで担保しなければいけなくなる。
2点目については、「MATERIALIZED VIEWを使う」など工夫のしようはあるが、前提としてイベントの処理が追いつかないほどの大量のデータを想定としているので簡単ではない。