キー、その名状しがたきもの
URL とキーについて考えていたら、URL に含むキーについていろいろ思うところがあったので雑に dump しておく。
暗黙のうちに REST を前提にしてしまっているところはあるが、意識的に REST とは文脈を切り離して素朴に書いてみる。
重複する部分もあるが、URL 側とキー側、それぞれの観点から書いてみる。
URL に求めること
自分が URL に求めることはいくつかあるが主に以下のようなことを求めている
- URL だけで目的のページにたどり着けること
- ページの簡易的な説明になっていること
- ASCII で構成されていること
それぞれの意図するところと重要度について簡単に書く。
[MUST] URL だけで目的のページにたどり着けること
あるページにたどり着くために「URL が指し示すページに行って、次に◯◯というボタンをクリックして……」みたいなことはやりたくない。
URL を入力したら一発で目的のページにたどり着いて欲しい。
(Addressable & Stateless であってほしい)
[SHOULD] ページの簡易的な説明になっていること
例としてブログの URL に含まれる path を考えてみる。
このとき、path が /articles/1234
となっているよりは /articles/introduction-to-key
とかのほうが実際にページを見る前に内容が予測できて嬉しい。
[MAY] ASCII で構成されていること
状況によるが、可能であれば ASCII だけで構成されていると嬉しい。
ASCII 以外を URL に含めるとエンコードされて URL が長くなりがちだし、可読性も下がってしまう。
とはいえ、ASCII 以外を含めるメリットが大きいときもあるので、そういうときは無理に ASCII にはしない。
キーに求めること
URL の中で path に含まれるキーにあたる部分以外について上の条件を満たすのはわりと難しくない。キーをどうするかが悩ましい。
キーに求めることは以下のようなもの。
- リソースを一意に特定できること
- リソースを一意に特定する以外の意味を持たないこと
[MUST] リソースを一意に特定できること
これはキーの定義上必須。
ただし、path に含まれる文字列がそのまま物理的に保存されている必要性は必ずしもない。
[SHOULD] リソースを一意に特定する以外の意味を持たないこと
キーとはリソースなりレコードなりを一意に特定するものであり、それ未満でもそれ超過でもない。
仮に auto increment な primary key を作成順の判別に使っているとしたら、それはキーの目的外利用になる。そもそも、作成順は作成日時から導出できるので整数列にしてしまうということは情報を損失している。
人間は整数を見るとどうしても比較したりソートしたりしてしまうので、この観点からはキーは乱数(UUID v4 やランダム文字列)だと嬉しい。*1
対立とバランス
上に書いたものの中で真っ向から対立するものがある。
- URLはページの簡易的な説明になっていること
- キーはリソースを一意に特定する以外の意味を持たないこと
URL はページの簡易的な説明になっていてほしいけど、URL に含まれるキーはリソースを一意に特定する以外の意味を持ってほしくない。
このふたつの間でどういう風にバランスを取るかというのが良い URL/キーをつくる上で重要だと思っている。
いくつかの例を考えてみる。
例1:商品管理アプリケーション
小売店の商品を管理するアプリケーションを考えてみる。
この店舗が扱う商品すべてに JAN コード(バーコード)が付与されているなら JAN コードは商品を代表する属性として妥当なように思われる。
そこで JAN コードを path に含むキーとして採用して /products/4569951116179
のような path にするのが良いかもしれない。
例2:石コレクターのための石管理アプリケーション
そのへんで拾ってきた石を管理するアプリケーションを考えてみる。
石はこの上なく自然のものだが、石に自然キーはない。
そこで何かしらのサロゲートキーを用意してやる必要があるが、ここで auto increment な整数を使うとキーに必要以上の意味を持たせてしまうことになるため、この場合は /stones/5f498e23
のようなランダムな文字列をキーとして含む path が良いかもしれない。
例3:薄い本管理アプリケーション
薄い本を管理するアプリケーションを考える。
薄い本には ISBN のようなものはないのでこれをキーにすることはできない。*2
とはいえ、本にはタイトルというその本を代表する属性がある。ただし、タイトルは重複する可能性があるのでそのままキーにすることはできないし、多くの場合、エンコーディングが必要な文字を含んでいるのでそのままだと path に含めて扱いにくい。そこで、本を代表する属性であるタイトルを元に新たにキーを考える。
たとえば /books/sekaiichi-kawaii-boku
のような感じ。
キーの選び方
例1で触れたように、リソースを代表する属性がありそれがキーである場合、代表する属性を path に含めるキーにするのが良い気がする。
例2で触れたように、リソースを代表する属性がない場合、人工的で意味を見出しにくいキーを path に含めるのが良い気がする。
例3で触れたように、リソースを代表する属性はあるがそれがキーではない場合、代表する属性を元に人工的で意味のあるキーを path に含めるのが良い気がする。
例1は自然キーを使った path、例2は意味のない人工キーを使った path になっていて対照的。
例3はそれらの中間でリソースを代表する属性から意味のある人工キーを作り出している。いわば、半自然キーとでも言うべきものになっている。
おわりに
URL を考えるだけでもこんなに苦労するのでプログラミングはむずかしい。
書きながらキーについて思うところが増えてきたので、近いうちにまた何か書くかもしれない。