RESTful API データモデリング

RESTful APIにおけるソフトデリート(論理削除)のデータモデリング:状態表現と操作設計

Tags: データモデリング, API設計, ソフトデリート, 論理削除, リソース状態

はじめに

多くのシステム開発において、データの「削除」は物理的な削除だけでなく、論理的な削除(ソフトデリート)として扱われることがあります。これは、誤削除からの復旧や、監査目的での履歴保持といった要件から採用されます。

RESTful APIを設計する際に、このソフトデリートされたデータをどのように表現し、クライアントからどのように操作できるようにするかは、データモデリングの重要な課題の一つです。不適切な設計は、APIの使いにくさ、保守性の低下、そしてデータの不整合を引き起こす可能性があります。

本記事では、RESTful APIにおけるソフトデリートデータの効果的なデータモデリングについて、その基本的な考え方から具体的な設計パターン、そして考慮すべき点までを解説します。

ソフトデリートが必要とされる背景とAPI設計上の課題

なぜ物理削除ではなくソフトデリートが必要となるのでしょうか。主な理由として以下の点が挙げられます。

これらの要件はシステム内部のデータ管理の課題ですが、RESTful APIはシステム内部のデータを外部に公開するインターフェースです。したがって、ソフトデリートの仕組みをAPIを通してどのように表現し、クライアントがそれをどのように認識し、操作できるように設計するかが課題となります。

具体的には、以下のような疑問が生じます。

これらの課題に対して、明確で一貫性のあるデータモデリングとAPI設計が必要になります。

ソフトデリートの状態をデータモデルにどう表現するか

ソフトデリートの状態を表現するための一般的なデータモデリング方法としては、以下の二つがよく用いられます。

  1. フラグによる表現:

    • is_deleted (boolean) のような真偽値フラグを追加する方法です。
    • true であればソフトデリート済み、false またはフィールドがない場合はアクティブな状態とします。

    json { "id": "item-123", "name": "商品A", "price": 1000, "is_deleted": false } json { "id": "item-456", "name": "削除された商品B", "price": 2000, "is_deleted": true }

    • シンプルで分かりやすい表現ですが、いつ削除されたかの情報は保持できません。
  2. タイムスタンプによる表現:

    • deleted_at (timestamp) のようなタイムスタンプフィールドを追加する方法です。
    • フィールドが null であればアクティブな状態、タイムスタンプが設定されていればその日時にソフトデリートされた状態とします。

    json { "id": "item-123", "name": "商品A", "price": 1000, "deleted_at": null } json { "id": "item-456", "name": "削除された商品B", "price": 2000, "deleted_at": "2023-10-27T10:00:00Z" }

    • ソフトデリートされた日時を記録できるため、履歴追跡や復旧時の情報として有用です。フィールドの存在/非存在で状態を判断するため、フラグよりも表現力が高いと言えます。

多くのケースでは、ソフトデリート日時も重要になるため、タイムスタンプによる表現 (deleted_at) が推奨されます。APIレスポンスにはこのフィールドを含めることで、クライアントはリソースの状態を正確に把握できます。

API操作の設計:取得 (GET)

ソフトデリートされたリソースの取得をどう扱うかは、設計上の重要な分岐点です。

デフォルトの挙動

多くの場合、クライアントがリソースを一覧取得したり個別取得したりする際に、デフォルトではアクティブなリソースのみを返すのが最もユーザーフレンドリーです。これは、通常の業務でソフトデリートされたデータに関心がない場合がほとんどだからです。

例: GET /itemsdeleted_atnull の商品のみを返す。

ソフトデリート済みデータを含める/除外する制御

特定のユースケース(例: 管理画面での論理削除済みアイテム一覧表示、監査目的など)では、ソフトデリート済みのリソースも取得したい場合があります。この要件に対応するためには、クエリパラメータを用いるのが一般的です。

例: * GET /items?status=active: アクティブな商品のみ(デフォルト) * GET /items?status=deleted: ソフトデリート済み商品のみ * GET /items?status=all: 全ての商品(アクティブとソフトデリート済み)

このように、statusinclude_deleted といったクエリパラメータで取得対象を制御できるように設計することで、様々なクライアントの要求に対応できます。パラメータを指定しない場合のデフォルト挙動を明確にしておくことが重要です。

個別のリソースを取得する場合(例: GET /items/{id})も同様に考える必要があります。存在しないIDの場合は404 Not Foundを返しますが、ソフトデリート済みIDの場合はどうするか? 選択肢としては: 1. デフォルトでは404を返す。クエリパラメータ(例: GET /items/{id}?include_deleted=true)を指定した場合のみソフトデリート済みのリソースを返す。 2. ソフトデリート済みであっても、IDが存在すれば常にそのリソースの状態(deleted_at の値)を含めて返す。

どちらの設計が良いかは、そのAPIの利用者やユースケースに依存しますが、通常は1.の「デフォルトでは見えない」方が、意図しない情報漏洩を防ぎ、多くのクライアントにとってはシンプルになる傾向があります。ただし、ソフトデリート済みかどうかを判定するために常に include_deleted=true で取得させる必要があるため、クライアントの実装は少し複雑になるかもしれません。2.の場合は、クライアントは取得したリソースの deleted_at フィールドを見て、それがアクティブかソフトデリート済みかを判断する必要があります。APIの利用者がソフトデリートの概念を理解している必要がある場合に適しています。一貫性の観点からは、一覧取得と同じパラメータ(例: status=all)で制御できるようにするのが良いでしょう。

API操作の設計:削除 (DELETE)

RESTfulな設計において、DELETE /resources/{id} は特定のリソースを削除する操作を意図します。ソフトデリートを採用しているシステムでは、このDELETEリクエストに対して、物理削除ではなくソフトデリートを実行するのが一般的です。

クライアントから見ると、DELETEリクエストを送信したリソースは「削除された」状態になります。この「削除」が内部的に物理削除かソフトデリートかを意識させないようにするのが、API設計における一つの考え方です。

DELETEリクエストが成功した場合、APIは 204 No Content200 OK を返します。レスポンスボディを含める場合は、ソフトデリート後のリソース表現(deleted_at が設定された状態)を返すこともあります。

DELETEリクエストを受け付けてソフトデリートを実行する際の処理: 1. 指定されたIDのリソースが存在するか確認する。存在しない場合は 404 Not Found を返す。 2. リソースがすでにソフトデリート済みでないか確認する。すでに削除済みのリソースに対して再度削除リクエストが来た場合の扱いは仕様によりますが、冪等性を考慮して 204 No Content200 OK を返すのが一般的です。 3. リソースの deleted_at フィールドに現在のタイムスタンプを設定し、データベースを更新する。 4. 更新が成功したら 204 No Content または 200 OK を返す。

API操作の設計:復元(PUT/PATCH/カスタムアクション)

ソフトデリートされたリソースを元に戻す、すなわち「復元」する操作は、RESTful APIでどのように表現すべきでしょうか。

どちらの方法を選択するかは、APIの設計思想やクライアントにどこまで内部構造を公開するかによります。カスタムアクションの方が、操作の意図が明確になり、よりドメイン固有の操作として表現できるため、APIの分かりやすさという点では優れているかもしれません。リクエストボディは不要か、あるいは復元に関するメタデータ(例: 誰が復元したかなど)を含めることができます。

関連リソースの扱い

ソフトデリートされたリソースに関連付けられているリソースや、逆にソフトデリートされたリソースを参照しているリソースをどう扱うかも考慮が必要です。

例えば、「注文」リソースが複数の「注文アイテム」リソースを持つ場合を考えます。「注文」がソフトデリートされた際に、「注文アイテム」も同時に非表示にすべきでしょうか、それとも「注文アイテム」はアクティブなままにして、参照元の注文が削除済みであることを示すべきでしょうか?

これは業務要件に強く依存しますが、一般的には、親リソースがソフトデリートされたら、関連する子リソースも(物理的には存在していても)APIからは見えなくなるように制御するのが自然な挙動です。

例: * GET /orders/{order_id}: order_id の注文がソフトデリートされている場合、デフォルトでは 404 Not Found を返す。 * GET /orders/{order_id}/items: 親である注文がソフトデリートされている場合、たとえ注文アイテム自体はアクティブでも、このエンドポイントは空のリストやエラーを返す。

関連リソースを含めて取得する際に、ソフトデリートされた関連リソースを除外するか含めるかの制御も必要になる場合があります。例えば、GET /orders?status=all&include_deleted_items=true のように、パラメータで細かく制御できるように設計することも考えられますが、パラメータが増えすぎてAPIが複雑にならないよう注意が必要です。

アンチパターン

ソフトデリートのAPI設計におけるいくつかのアンチパターンを挙げます。

まとめ

RESTful APIにおけるソフトデリートのデータモデリングは、単にデータベースにフラグやタイムスタンプを追加するだけでなく、APIリソースとしてその状態をどのように表現し、クライアントにどのような操作を許可するかを体系的に設計することです。

これらの点を考慮してデータモデリングとAPI設計を行うことで、ソフトデリートが必要な要件に対応しつつ、クライアントにとって使いやすく、かつ保守性の高いAPIを実現できるでしょう。

データモデリングは常にシステム全体の要件とAPIの利用シーンに合わせて最適な形を模索する必要があります。ここで述べた原則やパターンを参考に、皆様のAPI設計に活かしていただければ幸いです。