/note/tech

テーブルの created at にサービスのドメインロジックを持たせない

要約:

■ 1. 主張

  • created_at にドメインロジックを持たせない:
    • ActiveRecord のマイグレーションでは t.timestamps がデフォルト指定され、created_at と updated_at が付与される
    • これらのカラムはレコードの作成・更新を記録するメタデータとしてのみ利用する
  • where や sort の条件に使わない:
    • アプリケーション内で created_at を検索条件や並び替え条件として利用することは避ける
    • メタデータ以上の用途に広げると辛くなる

■ 2. created_at の意味の変質

  • created_at が指す日時:
    • created_at は「レコードが作成された日時」を意味する
    • 「レコードが記録されたイベントが発生した日時」ではない
  • 集計条件としての違和感:
    • 月間に発生した件数を集計する条件は「そのイベントが行われた期間」とするのが自然
    • 「レコードが作成された期間」を条件とするのは違和感がある
  • 不整合修正時に生じる矛盾:
    • 前月に発生したデータの不整合をレコード1件の追加で直す場合を想定する
    • created_at を集計条件にしていると、追加するレコードの created_at を前月にして insert する必要が生じる
    • そのような操作は違和感がある

■ 3. 対応方針

  • 専用の timestamp カラムを用意:
    • 何らかのアクションが行われた日付を取りたいのであれば、別途そのためのカラムを timestamp で持つ
  • イベントテーブルの例:
    • 注文が配送された日時を記録したいのであれば shipped_at を用意する
    • order has_one shipment のようにイベントテーブルを作り、その上で shipped_at を作る

■ 4. 想定される反論への応答

  • timestamp が不要なテーブルの発生:
    • タイムスタンプが不要なテーブルも出てくるという指摘はそのとおり
    • ActiveRecord がデフォルトで timestamp を付与するため、そのままにしているだけ
  • 必要になってから増やすという考え:
    • 必要になってからカラムを増やすという考えも正しい
    • ただし、それを判断できるメンバーがいつも開発フローの場にいるとは限らない
    • 結果として created_at を条件に集計ロジックを書く人が出てくる
  • 最初からカラムを作る利点:
    • 最初からカラムを作っておけば、将来開発するメンバーが迷わない
    • created_at や updated_at を条件にするならカラムをマイグレーションしろ、とAIエージェントに全員が依頼するとは限らない

■ 5. 他の論者の意見

  • Songmu 氏の記事:
    • 以前に拝見したことがあり、ほとんど同じ内容を書いてしまった
  • 神速さんの post から派生した議論:
    • neko314 氏の記事はとても丁寧な主張
    • hanachin 氏の記事は逆の意見で、必要になってから考えるという立場
    • そうした考えがあってもよい

■ 6. 背景と補足

  • 既存DBにRailsを被せた歴史:
    • 業務で扱っているサービスが既存のデータベースの上に Rails を被せている歴史を持つ
    • そうした事情があるためにこの考えに至ったのかもしれない
  • 実装の所在:
    • created_at と updated_at の実装は ActiveRecord::Timestamp にある

MEMO: