- 「データを削除「相当」の状態にして、あとから復元or削除できるようにしたい 」
- この要望に対して、論理削除を前提に設計を進めたところ、想像以上に考慮点が多く、最終的に設計を見直すことになりました
- 同じようなケースに遭遇したときの参考になればと思い、検討内容を整理します
当初の要望
要望: 担当者(アルバイト)がデータを削除できるようになっていると怖いので、管理者が確認してから削除できるようにしたい. 誤削除の場合は復元できるようにしたい。
初期案:論理削除
この要望から、以下のような設計を考えました。
想定フロー
- 担当者がデータを登録
- 担当者が論理削除(
deleted_atを付与) - 一定期間後、管理者が確認
- 問題なければ物理削除
一見するとシンプルに見えます。
実際に設計して見えた課題
しかし、実装を具体化すると次の問題が見えてきました。
- クエリが煩雑になる
- 毎回
deleted_at IS NULLを書く必要がある - JOINでも同様の条件が必要になる
- 毎回
- 制約との相性が悪い
- UNIQUE制約が扱いづらくなる
- 論理削除済みデータが制約に影響する
- 概念的に少し違和感がある
- 「削除」と言いつつ、実際にはデータは残り続ける
- 実態はステータス変更に近い
また、「とりあえず論理削除」はアンチパターン(https://www.slideshare.net/slideshow/ronsakucasual/52256922)なため、
別案を検討することにしました。
代替案:Archiveパターン
https://syu-m-5151.hatenablog.com/entry/2025/12/24/110101 を参考にして
監査ログではなくバックアップ用途でデータを残したいため、 Archiveパターンを検討しました。
しかし、Archiveパターンで設計してみると以下の問題が発生しました
- スキーマ変更時に、元テーブルとArchiveテーブルの両方を更新する必要がある
- 復元時にUNIQUE制約違反が発生する
- 元テーブル: hooカラム: x archiveテーブル: hooカラム: xのとき、復元するとユニーク制約に違反する
さらに、今回の要望では「復元」が前提となるため、結果的に論理削除の方が適しているように見えました。
参考: https://zenn.dev/nexx_hinata/articles/811dca42811032
- 論理削除が適切なケース
- 削除・復元が頻繁に発生する(ゴミ箱機能)
- 物理削除 + Archiveが適切なケース
- 削除は稀で、復元はさらに稀
改めて要件を見直す
復元が必要なためArchiveパターンは不適切。
一方で、論理削除は毎回 deleted_at IS NULL を考慮する必要があり、扱いづらさが残ります。
もっとスマートな解法がないかを考えるため、ここで一度立ち止まりやりたいことを整理しました。
参考になりそうな考えは https://zenn.dev/eftech/articles/eftech-soft-delete-as-technical-debtで
これをもとに考えると
- 非表示にしたいか → Yes
- データを残す必要があるか → Yes
- ライフサイクル → 不定期。
つまり、「削除」ではなく、データを残したまま状態を切り替えれば要件を満たせそうです。
最終的な設計
論理削除ではなく、ステータス管理を採用しました。
status: active / archived
- 非表示にする →
archived - 復元する →
active
is_deleteではなく、statusにすることで、(削除ではなく状態遷移)という意図が明確になり、
復元、ユニーク制約を満たせるためです
まとめ
論理削除を検討する際は、ユーザー視点 / 要件視点 / ライフサイクル視点 の 3 軸で分けて考えることが重要です。
今回のように一見すると論理削除が適しているケースでも、 「削除」として扱うのではなく、status による状態管理として捉え直すことで、 より意図が伝わる設計にできる場合があります。
参考URL
- 論理削除を選択した場合に発生する技術的負債について
- 論理削除のメリデメについて
- 論理削除の代替手段について(Archiveテーブルパターン, Temporal Tables(履歴テーブル), Event Sourcing, PostgreSQL パーティショニング)
- 論理削除批判派(バックアップ、ログはそれ用のテーブルなりDWHを作れ。 論理削除は不要)
- Archiveテーブルを選択したパターン
- 論理削除を選択したパターン(顧客の活動に随伴する上で論理削除の方が良い, 個別にSQLを記述しなければ削除フラグの弊害はなくなるのでは?)
- 「削除」という要件を分解する
- https://zenn.dev/eftech/articles/eftech-soft-delete-as-technical-debt