岩本隆史の日記帳(アーカイブ)

はてなダイアリーのサービス終了をうけて移行したものです。更新はしません。

続・こんな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

これで問題ないようにも感じますが、前回の日記で示唆したとおり、この例には以下の問題点があります。

  1. 「register」というユーザ名が使えなくなる
  2. 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. URIが長くなる
  2. パンくずリストと「/」との整合性がとれなくなる

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. キーが2つ以上のリソースでは破綻する
  2. サブドメインごとのトップレベルリソースをでっち上げる必要が出てくる

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をなめることによるパフォーマンス低下も気になりますが、まあどうにかなるんじゃないでしょうか(他人事)。

*1:ソースは忘れましたが、AmazonのサポートフォーラムでASINを10桁に決め打ちするプログラミングは避けるよう公式アナウンスがあったと記憶しています。