🐕

識別子の設計 Persistent Identifier design best practice

2022/06/05に公開約5,700字

識別子の設計に変更容易性を求めるべきではない。
ゆえに最初から正しい設計にする必要がある。

#識別子とはなにか?

Persistent Identifier 永続的識別子などと呼ばれている。
URIはスキーマやパスなど色々な構成要素で作られている。そのうち
Identifiers for the 21st century: How to design, provision, and reuse persistent identifiers to maximize utility and impact of life science data
の Introduction に定義される Local Identifier のことをローカル識別子または狭義の識別子と呼ことにする。
 PID, ID, idも識別子の同義語とする
本記事はローカル識別子の部分を中心述べる。パス情報などセマンティクスの規則については詳しく述べない。
セマンティクス(URIパターンなど)とローカルIDをあわせた概念を広義の識別子と呼ぶことにする.

#標準および参考文献

https://datatracker.ietf.org/doc/html/rfc8820
https://datatracker.ietf.org/doc/html/rfc3986

https://force11.org/info/the-fair-data-principles/

https://www.w3.org/2011/gld/wiki/223_Best_Practices_URI_Construction

https://web.archive.org/web/20210227041036/http://logd.tw.rpi.edu/instance-hub-uri-design

A Persistent Identifier (PID) policy for the European Open Science Cloud (EOSC)

https://fairdo.org/

Identifiers for the 21st century: How to design, provision, and reuse persistent identifiers to maximize utility and impact of life science data

https://www.w3.org/TR/2004/REC-webarch-20041215/

https://imi.go.jp/goi/goi-idcode/
これは、もう更新してないので、不適切になっている。
コードと識別子の境界条件はこの記事の定義の方が適切である。

#識別子の生成ルール

Study on persistent URIs, with identification of
best practices and recommendations on the topic for the
MSs and the EC

4.2.1  所有者をURIで表示することを避けよ

4.2.2 バージョン管理をURIをでするな。

4.2.3  URIの再利用をするな

4.2.4 オートインクリメントは避けよ

大規模なデータセットに対する新しいURIの作成は自動化される必要があり、そのプロセスは一意な識別子を生成することが保証されていなければならない。一意な識別子を生成することが保証されていなければならない。これを行う一つの方法は、新しいURIを生成するたびに単純にカウンターをインクリメントすることです。カウンターをインクリメントすることです。この方法は完全に実現可能ですが、以下のいずれかに当てはまる場合のみ推奨します。

  1. プロセスが決して繰り返されない。

  2. 同じ入力データに対してまったく同じURIを作成するためにプロセスを繰り返すことができ、新しいアイテムに対してのみ新しいURIが生成される。

生成ルール関数の入力データが変化する入力データに対して同じルールでURIを生成すると、入力データの状態によってURIが変化することになる。
すなわち、識別機能のために必要な情報以外の規則性をもって識別子を生成していた場合に登録前のデータの誤りを訂正するとIDが変化してしまう。
同じ入力データに対してまったく同じURIを作成することと、登録前の誤りを訂正しても識別子が変化しないこととはほぼ両立しない。
例外は、誤った登録情報からIDを生成し、その後に訂正イベントを記録するようなイベントソーシングや識別子の発行規則が意図する対象を呼び出すクエリとして存在する場合である。
イベントソーシングであるかどうかはデータベースの実装依存であるので、識別子からそれは推定できない以上
https://datatracker.ietf.org/doc/html/rfc8820 の Collisions アドホックな規約の標準化にあたる。
したがって、永続的識別子には呼び出す時に呼び出す主体(人間の場合が多いが機械もありえる)が得たい情報を識別するために必要な要素以外は入れてはいけない。言い換えると、識別のために使用する主体にとって識別子が指し示すオブジェクトの情報を知らない状態で既知の情報以外は永続的識別子に含んではならない。
生成時から閲覧時までの経緯を知る必要がないのに識別子にその情報を含むと、識別子を呼び出す主体にとってそれが未知の情報である場合に上記ルールに反することになる。

https://datatracker.ietf.org/doc/html/rfc3986#section-1.1 の Identifierに
識別子はそれとそれ以外のものを使われているスコープ内で識別するために必要な情報を具体化したものであると定義されている。

An identifier embodies the information required to distinguish what is being identified from all other things within its scope of identification.

これは識別子に識別以外の機能を含むことを禁止していないのだが、識別以外の機能をもたせるときには厳格な基準を設けるべきだと思っている。
それは 4.2.1 ~ 4 は識別する機能以外を識別子に持たせたときに起きる具体例であることが示している。
また、データベースの正規化の観点から述べると、識別子を一つの列としてそれに識別以外の情報が含まれていた場合、それは1次正規化がされておらず、キーと識別以外の情報が密結合していることになる。この点からも良くない。

##セキュリティー
実際に識別子に識別以外の機能を実現するための情報を含むときに起きるセキュリティーの問題をあげる。
セブンペイ IDが連番の部分を総当りで破られた。
銀行口座
発番が受付順なら、自分の前後の番号の名前を振り込み確認で知ることができるので、銀行窓口で申し込めば本人の顔がわかってしまう。

上記の通り識別以外の機能を識別子に持たせてはならないのだから識別子を付与する規則を作るべきではない。

#狭義のコード

では、識別子に識別以外の機能をもたせたい場合はどう扱うのか?

人間にも解釈でき機械的に認識可能な識別子として使えるが、識別以外の機能を持っている名前をコードと定義する。一旦コードは永続的ではないが識別子に含まれると考える。
郵便番号や受付の札番号など
コードと永続的識別子は違うので正しい設計方針も違う。コード永続性はスコープ内で十分。
受付の札番号のスコープは大抵、受付内かつ1営業日である。
一方識別子の永続性は、Cool URIs don't change Tim Berners-Lee, 1998 である。

コードと識別子の関連付け
コードの表現したい対象の識別子 : コード == 1:n
の関係が正しい。コードは識別子以外の情報が変化したら変わるので永遠に同じではない。
識別子同士の関連性は識別子で定義するべき。
コードの変更履歴を作りたいなら、各々のコードごとに識別子を発行し、コードが指し示したい対象の識別子と関連付けるべき。コードは検索クエリにはなり得るが、無限の永続性を担保できないので未来永劫のこる関連付けの記録としては使えない。

スコープが変化する可能性
大規模なシステムになるほど、識別子が利用されるスコープが変化して他のスコープから対象が移動する可能性が高くなる。したがって、大規模になればなるほど、識別子は全ての存在に対してランダムで発番するのが正しい設計となる。

#セマンティクス

ではパスなどセマンティクスはいつ使うべきなのか?

https://zenn.dev/articles/explore/1
https://zenn.dev/articles/explore/2
みたいなURIがあったとして、/1が一番オススメで/2が二番目におすすめなら、この命名が適切である。
おすすめは閲覧時のおすすめを閲覧時に生成してそれが正しいとされている間に閲覧するのであり、たとえ古い情報になっていたとしても、閲覧者にとっては生成時の情報が変化しないとみなされる。
連番であるが、"同じ入力データに対してまったく同じURIを作成するためにプロセスを繰り返すことができ、新しいアイテムに対してのみ新しいURIが生成される。"場合であるので連番で問題ないのだ。
この例がそうなっているか検討する。
おすすめ記事群の上位n件がが入力データとして与えられる、記事一つ一つのが変化しても全く同じURIを返す。一番おすすめアイテムは一度のみ生成される。なので適合している。
同じ入力データとは、識別する対象の選択条件が同じかどうかについて述べているのだろう。識別子と内容が全て同じデータかどうかがこの規則においての同じ入力データかの判定基準ではない。
セブンペイの例で言えば、識別する対象の選択条件はAさんのみで、n番目にユーザーIDを発行した人ではない。連番でユーザーIDを発行していた場合には、IDの発行順も与えないと全く同じURIを作成することはできない。
識別に最低限必要な情報以外を与えないと同じURIを生成することが出来ないのは悪いとすれば、クエリ的なURIと一番アトミックな識別子に紐づく識別子がない識別子の場合分けをせずに、良いか悪いか判定可能である。
この判定基準ならば例に出したzennのURI設計は正しいと言える。

セマンティクスは、識別する条件を数字ではない文字列で表現することで読み取りやすくなり、なおかつそれが識別のための最低限の条件である場合に許可されるべきだ。
articles は記事の集合とそれ以外を識別することに使う。 あるオブジェクトが記事ではなくなったら、URIが変化する。その場合記事ではないがなにか別のお知らせみたいなオブジェクトに変化して notifications みたいなセマンティクスが割り当てられるんだろう。 
記事とお知らせオブジェクトの同一性をローカル識別子で担保したい場合は記事とお知らせの和集合をスコープとしてURIを設計する必要がある。
一番目に書かれた記事を呼び出したいなら article/sort_by:created/1 のようなURIが有効なので、この場合は積極的にセマンティクスを使うべきである。

#雑感
EU規則 4.2.4の1のプロセスは絶対繰り返さないことはどんなシステムでも絶対に保証出来ない以上、とても大規模なシステムで考えるべき基準は2のみなのではないか?

住所と住居表示はコードなのか識別子なのか?
住所は前からあったが、住居表示は郵便配達のためにつけられた。
配達ルートが決まっていたら、全部連番でいいじゃないかと思うかもしれないけれども、
そうすると、配達ルートを決めたあとに任意にルートを変更しなければいけなくなったらまた振り直さなくてはならない。
また、順番が物理空間上の距離ではないが、住所と住居表示を組み合わせれば、連番ではないもの同士の距離がおおまかに推定可能である。
従って、コードであって、連番以外の距離関係を推定可能な機能を持っているのである。

コードと永続的識別子を分けて考えてみたが、コードもある永続的識別子群を呼び出すクエリを持っているオブジェクトに対しての永続的識別子と考えれば、全ての識別子は永続的識別子であるべきだ。
受付番号は呼び出された日時によって、予約された受付全体で一意な永続的識別子から呼び出した日時の紐付けられた受け付けを呼び出すためのクエリオブジェクトを呼び出す永続的識別子であると言える。

Discussion

ログインするとコメントできます