続・こんなURI 設計、どう?
先日書いた「こんなURI 設計、どう?」の続きです。
例:商品ブックマークサービス
URI設計の例として、商品ブックマークサービスを対象に考えてみます。提供するおもなリソースは、以下の5つです。
リソース名 | パンくずリストのイメージ |
---|---|
トップレベルリソース | トップ |
ユーザ登録画面リソース | トップ>ユーザ登録 |
ユーザ別ブックマークリソース | トップ>iwamotのブックマーク |
ブックマークリソース | トップ>iwamotのブックマーク>Webを支える技術 |
商品リソース | トップ>Webを支える技術 |
うち商品リソースについては、はてなブックマークのエントリページ(例:http://b.hatena.ne.jp/entry/twitter.com/)みたいなものとお考えください。商品ブックマークサービスでは、entry以下のURI断片の代わりに、Amazonの商品コード(ASIN)を用いることにします。
フラット方式の問題点
『Webを支える技術』で示されている設計例(フラット方式とよぶことにします)に準ずるならば、下記のようなURIが導かれます。
リソース名 | URIの例 |
---|---|
トップレベルリソース | http://example.jp/ |
ユーザ登録画面リソース | http://example.jp/register |
ユーザ別ブックマークリソース | http://example.jp/iwamot |
ブックマークリソース | http://example.jp/iwamot/4774142042 |
商品リソース | http://example.jp/4774142042 |
これで問題ないようにも感じますが、前回の日記で示唆したとおり、この例には以下の問題点があります。
- 「register」というユーザ名が使えなくなる
- ASINと重複しそうなユーザ名が使えなくなる
1についてはそういう仕様だとみなせばよいだけですが、2は対処不可能といっても過言ではありません。ASINの形式は不定だからです。たまたま現時点では、ASINが10桁の英数字に決まっているようにみえますが、今後もずっとそうとはかぎりません*1。
したがって、なんらかの識別子を付けることで「http://example.jp/」直下の文字列がユーザ名を指すのかASINを指すのか判別させる必要があります。
一覧リソースをはさむ方式の問題点
識別子の一案として浮かぶのが、各キー値の上の階層に一覧リソースを追加する方式です。下表をご参照ください。
リソース名 | URIの例 |
---|---|
トップレベルリソース | http://example.jp/ |
ユーザ登録画面リソース | http://example.jp/register |
ユーザ別ブックマークリソース | http://example.jp/users/iwamot |
ブックマークリソース | http://example.jp/users/iwamot/items/4774142042 |
商品リソース | http://example.jp/items/4774142042 |
これで「iwamot」や「4774142042」がユーザ名なのかASINなのか確実に判別できるようになりました。
しかし、この方式にも以下の問題点があります。
1はトレードオフだと納得できるとしても、2は少なくとも僕は納得できません。「http://example.jp/users」でどんなリソースを提供するのか、あるいはしないのか、後付けで考える必要が出てくるからです。当初の要件とリソースの種類やパンくずリストの構成が変わるとすれば、そんな設計方式はポンコツでしょう。よって、この方式は却下となります。
サブドメイン方式の問題点
そこでどうしようかと考え、浮かんだのが、前回の日記に書いたサブドメイン方式でした。
リソース名 | URIの例 |
---|---|
トップレベルリソース | http://example.jp/ |
ユーザ登録画面リソース | http://example.jp/register |
ユーザ別ブックマークリソース | http://user.example.jp/iwamot |
ブックマークリソース | http://user.example.jp/iwamot/4774142042 |
商品リソース | http://item.example.jp/4774142042 |
これなら、キーの重複もないですし、パンくずリストとの整合性もとれます。しかし、この方式にも以下の問題点があることが分かりました。
1は、たとえばユーザのプロフィールリソースを後で追加したくなった場合、「http://user.example.jp/iwamot/profile」と「http://user.example.jp/iwamot/4774142042」とがコンフリクトするという意味です。
それより何より、2の問題点によって、さきほどの一覧リソースをはさむ方式と同じく、このサブドメイン方式もポンコツだということになります。
識別子結合方式がよさそう
つまることろ、識別子とキー値を任意の区切り文字で結合させるほかないのではないでしょうか。たとえば「.」で結合させる場合、下表のようになります。
リソース名 | URIの例 |
---|---|
トップレベルリソース | http://example.jp/ |
ユーザ登録画面リソース | http://example.jp/register |
ユーザ別ブックマークリソース | http://example.jp/user.iwamot |
ブックマークリソース | http://example.jp/user.iwamot/item.4774142042 |
商品リソース | http://example.jp/item.4774142042 |
問題点をしいてあげるならば、やはりURIが長くなるということでしょう。僕はトレードオフとして納得できますが、そうでない方はWikipedia方式なんかどうでしょうか。
おまけ:Wikipedia方式
Wikipedia方式とは便宜的につけた呼称ですが、要するに、キー値が実際にコンフリクトした場合にのみ識別子をつける方式のことです。
「hogehoge」がユーザ名かASINのいずれか一方にのみ使われている場合は:
http://example.jp/hogehoge
としておく。
どちらにも使われた場合のみ:
http://example.jp/hogehoge(user) http://example.jp/hogehoge(item)
とする。
実装が面倒そうですし、両DBをなめることによるパフォーマンス低下も気になりますが、まあどうにかなるんじゃないでしょうか(他人事)。