リソースモデリングパターンをまとめています。

Webアプリケーションについて、RESTfulなURL・リソース設計のパターンを見出すことで、

  • どのパターンかを判断するだけで、既存の Good Practice が適用できる
  • 名前をつけて呼べるようにしたい
  • Railsなどのフレームワークで簡単に適用できるようにしたい

ということを目指しています。

  • ほんとうに役立つか
  • これはパターンと言えるのか
  • もっと他にもある
  • だいぶ粒度がバラバラ
  • 名前の付け方(パターンは名前重要)

など、ぜひご意見をください。

パターン

Collection & Member Resource パターン

Singular (Singleton) Resource パターン

Filtered Collection パターン

Filtered Subresource パターン

Multi-member Resource パターン

Partial Resource パターン

Transaction Resource パターン

Session Resource パターン

Private Resource (Namespace) パターン

Implicit Collection パターン

リソースの大分類

URL(パス)の構造を目安として分類したものです。

コレクションリソース

Collection & Member Resource パターン

メンバーリソース

Collection & Member Resource パターン

Implicit Collection パターン

単数 (Singular, Singleton) リソース

Singular (Singleton) Resource パターン

補助リソース

Collection & Member Resource パターン

アルゴリズムリソース

Filtered Collection パターン

静的リソース

  • 画像とかJavaScriptとかCSSとか
  • もともとWebに静的・動的の区別があるわけではないので、Railsや、Webアプリケーションフレームワーク特有の分類かもしれない
  • GETのみ

ルートリソース

  • 他のリソースへのナビゲーション的役割 (GETのみ)
  • もしくは、サービスの主となるリソース (コレクションリソースが多い)の別名

Implicit Collection パターン

/{id}

どのようなパターン?

Collection & Member Resource パターンから、コレクションリソースの部分を取り去ってパス階層を縮めた形。

例えばこのようにする。

/users/1/articles   → なし
/users/1/articles/2 → /users/1/2

コレクションリソースを省略する理由としては、

  • コレクションリソースが不要
  • コレクションが何であるかは自明
  • パスを短くしたい

などが考えられる。

コレクションリソースが不要

「コレクション全体」へのアクセスが必要ない場合。 TwitterやGitHubは、ユーザー数が膨大で「ユーザー全体」というリソースを提供する意味がなく、個別のユーザーリソースだけがあればよい。

/users/tkawa → /tkawa

別のリソースを作る場合、ユーザーリソースの名前と衝突しないように注意。

また、Factoryの役目を担うリソースがなくなるため、リソース作成の手段を考慮する必要がある。

コレクションが何であるかは自明

年月日の例。

/years/2012/months/12 → /2012/12

ルートリソースを代用

/users/tkawa → /tkawaのように最初のパス階層を省略するときは、ルートリソース/をコレクションリソースとして使うことも可能。

Rails routes.rb の書き方

resources :users, path: ''

Private Resource (Namespace) パターン

/my
/my/{resource}

どのようなパターン?

Singular Resource パターンの特別な場合で、「自分自身」を指すリソース。

人(セッション)によって違うリソースを指すことを明示するために名前空間を分ける、という用法に着目する場合は、Private Namespace パターンとも呼べる。

Rails routes.rb の書き方

resource :my do
  resources :posts
end

もしくは名前空間的用法

namespace :admin do
  resources :posts
end

コラム:/my って変?

/myというURLはちょっと奇妙に感じるかもしれません。/my/postsだとわかりやすいのですが。

/meを選ぶ場合もあります。しかしその場合/me/postsがちょっと奇妙です。

どちらを選ぶかは好みですが、どちらかに統一するのがよいでしょう。また、/my/postsだけが存在して/myが存在しないのは、あまりよくありません。

Session Resource パターン

PUT /session (→ログイン)
DELETE /session (→ログアウト)

どのようなパターン?

認証のセッション自体をリソースととらえる。Railsでいう「モデル」ではないリソースの典型例。

Railsの認証Gemを使った設計例ではすでに一般的に採用されている。

  • Devise
  • Sorcery
  • OmniAuth

リソースの分類からみる

上記の例だと単数リソースで実現されている。さらに自分に紐付いているものであるとみなして、

PUT /my/session (→ログイン)
DELETE /my/session (→ログアウト)

とすると Private Resource パターンとなる。ただしログイン前から /my が存在するのは少し不自然かもしれない。

コレクションリソースを使って

POST /sessions (→ログイン)
DELETE /sessions/{id} (→ログアウト)

と実現することもできるが、こうした場合のidは何なのかよくわからない。

Transaction Resource パターン

POST /transactions
 ↓
PUT /transactions/123
 ↓
PUT /transactions/123/committed

「Webを支える技術」p278より引用

実際のシステムでは、より複雑な処理、たとえば複数のリソースにまたがった変更をひとまとまりに扱う、いわゆるトランザクションが必要になるケースもあるでしょう。

主にCollection & Member Resource パターンを用いたトランザクションの実装。

ウィザードなどにも適用可能で、モデルでないリソースになりうる。

http://qa.atmarkit.co.jp/q/2555#answer_15110id:moro さんの回答より引用

やり方はいろいろありますが、データインポートなど複数のリソースに影響を及ぼす、バッチ的な動きをさせたい場合には「トランザクションリソースを作る」という考え方でリソース設計するようにしたところ、いろいろ捗りました。

たとえば今回の例でも「インポート処理: import_job」を作るという風に考えると、ルーティングは

resouces :import_jobs

としておき、

  • GET /import_jobs/new (ImportJobsController#new) でインポート用のフォームなどを表示し、
  • POST /import_jobs (ImportJobsController#create) で実際のインポート処理をはじめるなど、いろいろやりようがあります。

私が実アプリで取り組んだときは、インフラの都合上非同期にインポートジョブを実行させたかったので、createアクションの中でいったん必要な情報をDB(import_jobsテーブル)に保存しました。その後でワーカーに流し、

  • GET /import_jobs/:id で進捗状況や実行結果を表示

という作りにしました。ポイントは『処理の固まり』を作るのもリソース作成としてとらえることです(なんでもかんでもやると妙ちくりんですが)。

Partial Resource パターン

/users/123/name,email
/users/123?fields=name,email

リソースの部分取得、部分更新のために一部の属性(フィールド)だけを提供するもの。

Multi-member Resource パターンと形が似ているが、Multi-member Resource パターンは複数のメンバーリソースを列挙するものであるのに対し、Partial Resource パターンは1つのメンバーリソースのさらに一部分を取り出すものである。

通信量に制限のある環境などで有用。

使用メソッド

GET, PUT, DELETE

Multi-member Resource パターン

/users/123,124
/users/123-133

メンバーリソースの派生形。複数のメンバーリソースを一度に取得、更新、削除するために使用できる。

前者の列挙型であればどんなIDでも利用できるが、後者の範囲型は数値などの限られたIDでしか利用できない。

使用メソッド

GET, PUT, DELETE

ステータスコード

一部のリソースだけ更新・削除に失敗したとき、 207 Multi-Status の使用を検討する。