「論理削除」をどう扱うか
はじめに
システム開発ではよく「論理削除」が採用されることがあります。この記事では論理削除の必要性と、代替案について考察します。
論理削除とは
あるデータ、例えば、記事やユーザーデータを「削除」する場合に実際にレコードを削除せずに、例えば「削除済」というフラグを立てることで削除したように見せる、それが論理削除です。これに対して、物理的に削除する場合を「物理削除」と呼びます。
雑学ですが、本来は「削除」で良かったのに「論理削除」という概念が出た結果「物理削除」という言葉が生まれた、このような用語をレトロニムと呼びます。
論理削除を使うメリット・デメリット
論理削除を使うメリット・デメリットは次のようになります。
メリット
誤って削除されたデータを復元しやすい
デメリット
「削除フラグ」という名前が直感的ではない
フラグで実装すると組み合わせ爆発する可能性がある
クエリに論理削除かどうかの考慮が漏れるとバグが出る可能性がある
デメリットを多く挙げているように、自分は基本的には削除フラグには否定的で、もっと良い方法があると思っています(具体的な方法は後述します)。
まず論理削除をしたい理由を確認する
自分は論理削除という言葉が出たら、本当は何をしたいのかを確認します。
次のQiitaの記事にあるように「論理削除した行はどういうときに参照するか」「論理削除した行が復活することはありますか」「論理削除した行は最終的にどうなりますか」といった理由を聞くことが重要です。
まず「ステータス」を検討する
仮に論理削除をしたい理由が納得行くものだったとしても、自分は少なくともフラグを使った設計は避けています。なぜなら自分はその昔、削除フラグを使った設計で痛い目にあったことがあるからです。
自分が昔設計したシステムでは最終的に次の3種類のフラグを使っていました。今から考えるととても酷い設計ですね・・・。
アカウントが削除されたかどうか
アカウントが有効かどうか
サービスが有効かどうか
この3つのフラグの組み合わせは8種類ありますが、矛盾した組み合わせもあります。「アカウントが削除されたがアカウント有効」という組み合わせはありません。削除フラグで実装するとこの手の問題が起きがちです。
フラグの代替案としてお手軽なのは「ステータス」です。型で言えばbooleanではなくenumです。例えばアカウントの場合、次のようなステータスが考えられます。
仮登録: アカウントを新規登録したが「確認」されていない状態
「確認」はメールアドレスが未確認、本人確認がまだなど
この状態ではシステムが使えない、あるいは制限されることがある
登録済: 「確認」が終わって使える状態
休止中: 一時的に無効化している状態。再開すれば「登録済」に戻る
解約済: 恒久的に無効化した状態
パターンが2つしか考えられない場合もありますが、その場合は一旦「有効」「無効」としておけば十分です。「アカウントの削除フラグがオン」よりも「アカウントのステータスが無効」の方が理解しやすいからです。名前が分かりやすいというだけでも、削除フラグを使わない理由としては十分です。
なお、GDPRの「忘れられる権利」のように個人情報を完全に消さないといけない場面もあります。そのようなときは住所や名前を無効な値で埋める、あるいはデザインパターンにおける「Null Objectパターン」を使って、解約専用のユーザを作るのが良いでしょう(GitHubのghostのように)。
その他の解決法
ステータスは論理削除の一番手軽な代替案ですが、その他にもいくつかあります。
履歴、あるいはアーカイブテーブルに移動して削除する。
ログに書き出して削除する。
deleted_at列(削除日時)を使う
ただ本質的には削除フラグと変わらないのでは?と思っているのでお勧めしていません。
Laravelのようにdeleted_atがフレームワークでサポートされているのなら選択肢の1つとして考えてもいいかなという程度。
そもそも削除しない(イミュータブルデータモデル)
CRUDのC(Create)とR(Read)だけ使って、U(Update)とD(Delete)は使わない方法です。
この場合、削除ではなく「削除イベント」を扱います。
様々な方式がありますが、どれを採用するかはシステムや要件によって変わります。復旧の頻度や重要性などを考慮して、個別に検討してください。