Railsの誤解:CRUDはRESTじゃない!

以下はNick Sutterer氏が2010年10月28日に自身のブログに投稿した、"Rails Misapprehensions: CRUD is not REST! "の翻訳です。本人の許可を得て掲載します。


Rails Misapprehensions: CRUD is not REST!
http://nicksda.apotomo.de/2010/10/rails-misapprehensions-crud-is-not-rest/


RailsとRESTについて調べている間、二つのことがよくわかった。

  • RailsでRESTがどうなっているのか、他と比べて、明解で、基礎的で、「印刷された」解説を見つけにくい。数千のスクリーンキャストを見てきたが、この素晴らしいガイドが一つあるだけだった。
  • みんなCRUDとRESTを混同している

とりわけ後者は僕を困らせたが、あるチームをコーチするとき、CRUDとRESTをきれいに分けることがRESTfulが本当の意味を理解させるのに役立った。HTTPの制約でしかないということだ。

この知見を共有したいと思う。

CRUDって何?

すべてのアプリケーションは、一つの目的を持っている。ビジネスオブジェクトを支ることだ。(Maintaining business objects) 基本的に、フォームやリストやその他のWebのUI要素を使って、created、displayed、updated and deletedがなされるモデルを扱えるということだ。

もし君のRailsアプリがビールを管理するものなら、BeersContorollerにshow、update、createといったアクションを書くだろう。これらのアクションは、おおかたフォームを表示したり、モデルに属性を保存したりする。

ブラウザでこれらのフォームにアクセスするのに、次のようなルート(routes)を書くだろう。

  • /beers/show/:id
  • /beers/new
  • /beers/delete/:id

そして、先に述べた、対応するコントローラのアクションにマッピングさせるだろう。

これがいわゆるCRUDインターフェースだ。記述的なルートやフォームに大抵は退屈なコードを持ったアプリケーションだ。これには一つの運命がある。Createされ、Retrieveされ、Updateされ、Deleteされるという運命だ。

いいかい、これがCRUDだ!

じゃあ、RESTは何?

数年前、新しい標準が現れた。それはRESTと呼ばれ、以下に挙げる、主要な原則を持つ。

  • RESTの中心的な概念は、リソース(resource)で、一つまたはそれ以上のビジネスオブジェクトを表現するデータ構造だ。
  • また、RESTは簡潔(clean)で、単純な(simple)、そう、"RESTful"なURLを推奨する。
    • 例)/beers/1
  • これらのURLはHTTPの動詞(GET/PUT/POST/DELETE)を使い、何を「する」かはHTTPリクエスト中にエンコードされる。、URL中にエンコードはされない。
  • 何かの決まりによってcontent negotiationが要求される。リソースをHTMLやXMLやそれ以外の形式でリクエストすることができる。

これがRESTが推奨していることだ。つまり、どのようにしてそれをするかについては言及していない。それは我々が決めるということだ。

どんな違いがあるの?

僕は、RESTがCRUDとどう違うのが不思議に思っていた。厳密に言うと、二つの概念には二つの同じ疑問がある。

  • どのリソースを対象としているのか?
  • そのリソースに何をしたいのか?

類似点はさらにある。

  • リソースCRUDにおけるビジネスモデル(business model)だということ
  • CRUDを表現するルート自身が動詞(例えばeditやshow)を含んでいるが、RESTではこの情報をHTTPリクエストでエンコードしているということ

つまり、本当の「違い」はこうだ。僕らのCRUDコードは既に振るまいを実装しているのに、RESTはURLについての標準を推奨しているだけなんだ(僕はこれを高く評価しているよ!)。

RailsのREST

RailsのRESTコードがしてくれることは、"RESTful"なヘルパーを提供してくれるだけだ。

本当のロジック、CRUDコードは君に任されている。(José’s InheritedResourcesを使わない限り)

RailsはRESTをどう補助してくれるのだろうか?

  • ルーティングヘルパーのresourcesを提供してくれ、たくさんの「名前付きの」RESTfulルートを生成してくれる
  • form_for のようなヘルパーが、モデルとリソースの内部状態にしたがって、RESTfulなURLへ自動的にリンクしてくれる

RESTについて必ずしっておくべきこと

RESTで本当にクールなことは、素敵な、標準化されたURLを別にしても、パブリックなAPIがタダで手に入ることだよ!(a public API for free)」

次のようなことが可能になる。

  • /beers/1.xmlGETを使うと、素敵にフォーマットされた、モデルのXML表現にクエリ発行ができる。君のコードがそれを提供していればね
  • /beersにPOSTを使うと、POSTパラメータに追加して新規リソースに属性を与えることができる

コードを書く必要も特別なバインディングコードを使う必要もない。HTTPを使うだけでデータベースとファックできるんだ。スゴくない?

Nick、何が問題なんだよ?

OK、要約すると、ロジックは依然としてCRUDだということだよ。ロジックは「RESTful」じゃない。

短いURLについてや異なるリクエストタイプについて考えるのであれば、RESTを参照するべきだろう。
それ以外(The rest)は...CRUDだ。(言葉遊びさ!)(訳注:RESTと「残り」という意味の the restをかけている。)
RESTは、僕らがCRUDを実装した上にあるただの薄いレイヤでしかなく、URLとリクエストのフォーマット方法を教えてくれるだけだ。

CRUDとRESTを混同したままでは、RESTが格別で「クール」なことを決して理解することはないだろう

rofhのコメント

RESTはURLが短いとか長いとかいったこととは無関係じゃないかな?次のリンクを置いておくよ。

http://en.wikipedia.org/wiki/HATEOAS
(これはRailsがミスしていて、'restful'とRESTの主なdiffについてだ。)

http://www.ics.uci.edu/~taylor/documents/2002-REST-TOIT.pdf
Royの原点となっている理論。みんな読んだふりだけしていることは事実だ。(:

たぶんもっとも実践的なソースはこれ。
http://www.slideshare.net/guilhermecaelum/rest-in-practice

実装については、Cloudkitのものがかなり好きだ( http://getcloudkit.com/rest-api.html )。これはリソースのメタ情報をボディじゃなくてヘッダの方に含めているけれど。

Railsでの実装は、RSoCの課題だ。(訳注:RSoCとはRuby Summer of Code のことと思われる。)http://github.com/caelum/restfulie に見つけることができる。まもなく利用できるようになるのを望んでいる。

Juan Ignacio Pumarinoのコメント

いい記事だね。だけどicebergはRESTfulとは実際「HTTPの制約以上のものだ」と言っているよ。僕はこちらの方が正確で明解な説明だと思う。
http://martinfowler.com/articles/richardsonMaturityModel.html

Adamのコメント

Nick、二つの説明をするよ。

リソースは必ずしもビジネスオブジェクトと同じというわけじゃない。一時的なリソースに対応するビジネスオブジェクトが存在せずともビジネスオブジェクトを変更することがありうるんだ。

例をひとつ言おう。BlogモデルとUserモデルがあるとして、UserにBlogを購読することを許可させたいとしよう。Railsだと普通はUserをBlogに追加するか、BlogをUserに追加することになると思う。(/blogs/12/subscribers か /users/233/subscriptions にPOSTするか、簡便さや対称性を求めて両方を採用することもできる。)

これがCRUD操作に必要になる。ここで自分のUserかBlogをupdateしたいとしよう。

RESTは僕たちにまったく異なる視点を提供してくれるんだ。もし、RESTアーキテクチャスタイルに従いたいならば、徹底してやってSubscriptionリソースを定義してやればいい。リソースはビジネスオブジェクトに対応するというよりもむしろ、ビジネスオブジェクトに対してなされる別個の操作を表現しているだろうね。
Subscriptionモデルはこんな感じの表現になると思う。

{blog”: “http://example.com/blog/23123″,subscriber”: “http://example.com/user/32212312″,
}

特に、僕が個々のsubscriptionメンバーを表すURLを通していることに気づくと思う。
これは意図的なんだ。RESTではURLを通して「常に」他のリソースを特定(identify)する。
Railsはこれができないし、このことがRailsの可能性を深刻なまでに弱めてしまっている。
URL一つは単にネットワークのアドレス一つなんだ。リソースが自分のネットワーク上にあるかどうかを知っている必要なんてあるだろうか?それがユーザでなければならない理由があるだろうか?コンテンツアグリゲータや そのような、subscribersがプッシュ更新を受けるよう振る舞うものであってはならない理由があるだろうか?URIによる関連(association)(それはポリモーフィックであったり、ダックタイピングの柔軟性によるものだ)はソフトウェア開発における次の偉大な躍進だ。

君の主張の「また、RESTは簡潔(clean)で、単純な(simple)、そう、"RESTful"なURLを推奨する。」というのは完全に間違っているよ。単純で簡潔なURLが有効なのは、人間がコミュニケーションするための道具としてだけだ。それは価値があることだが、それ自体はRESTじゃない。

厳密にRESTfulであるというのは、自分自身に次の問いかけをしてみればわかる。URIをすべてGUIDに置き換えてしまったとしたら何が起きるだろうか?と。上に挙げたsubscriberリソースの代わりになるものとして、次のようなのも等しく有効だ。

{blog”: “http://example.com/40DBED2E-E2DB-11DF-89B9-E9D3DFD72085″,subscriber”: “http://example.com/6258C97C-E2DB-11DF-B28E-18D4DFD72085″
}

これを通常のRailsアプリで使うとすれば、次のようにするだろう。

{blog”: 23123,
“subscriber”: 32212312
}

それから、restfullie(http://restfulie.caelum.com.br/) をRailsで使わなかったとしたら、RailsAPIは全然RESTでないと思う。鍵となる、欠けてる重大事項は「ステートエンジンとしてのハイパーテキスト」だ。(Hypertext As The Engine Of State)

chrisのコメント

free public apiでなければRESTfulにする意味がないの?

Electron-libreのコメント

Nickに賛成。RESTの概念は、僕のWebクライアントの考え方を変えてしまった。今はアプリを書くときは、REST APIクライアントの視点で考える。そうすると優れた設計をしようとするし、一貫したURI規則を保つことができる。

manuelのコメント

僕が考えるに、概念やパターンやロジック(CRUD)と、標準(REST)を区別することに問題があって、それで同じように考えようとしてしまうんだと思う。
Nickが指摘したように、このことを意識して正確に使うことで、全ての利点が得られるのだと思う。

dのコメント

君は「RESTはHTTPの動詞(GET/PUT/POST/DELETE)を使用する」と言っているけど、実際はRESTアーキテクチャはHTTPに特別限ったことじゃないよ。HTTPはRESTアーキテクチャのシンプルな実装だよ。RESTはHTTPより上位に存在だ。RESTを実装したHTTP以外の技術を使う可能性があるし、存在する可能性がある。

Nickの返信

@d:そうだね。「リソースはHTTPヘッダによってエンコードされた動詞によって操作されるが、必ずしもHTTPに限定されてはいない。」と書くべきだったね。君はRESTに適した別のプロトコルを知ってるかな?僕はすごく見てみたいよ。