{
  "$type": "com.whtwnd.blog.entry",
  "theme": "github-light",
  "title": "共有lexicon設計に関する雑感",
  "content": "\natprotoサービス開発者を中心とした、[lexicon.community](https://docs.smokesignal.events/blog/community-lexicons/)という取り組みがある。これはlexiconを特定のサービスに結び付けずに共有しようという試みだ。\n\nが、個人的にはこれに勝手な期待をかけて勝手に裏切られたため、否定的な態度を取ることが多い。先方に取っては迷惑千万だろうが、ダシに使わせてもらって、自分が望んでいたのはどういうものだったかを残しておこうと思う。念の為言っておくと、方向性とか目指すものが異なるという話であって、やっていることがしょぼいとか思っているわけではない。\n\nlexicon.communityの[最初のlexiconの議論](https://github.com/lexicon-community/lexicon/pull/12)を見た印象は、「普通にサービス作ってるみたい」というものだった。もちろん汎用性を強く意識したりはしているが、基本的には参加者が思い思いのユースケースを想像して、その中で良いlexiconを作ろうとしている。lexicon.communityはまだゴールも[議論している](https://github.com/orgs/lexicon-community/discussions/37)段階のため、あくまで個人的な印象に過ぎないが、共有財産であることを明示したlexiconを作るということが重要なのだと思う。\n\n一方で、lexicon.communityの存在を初めて知った時、中身を見る前に想像した可能性は、例えば以下のようなものだった。\n\n* サービス自体を表すメタなスキーマ\n* サービスの内容に依存しないプロトコルユーティリティ\n* 多くのサービスで共有できるデータ\n* 確立済スキーマの共有\n* 汎用性の高い機能\n    * 全ての機能を包括する最小公倍数的なアプローチ\n    * 最低限の機能だけを定義する最大公約数的なアプローチ\n    * サービスによる拡張方法を予め定義するテンプレート的なアプローチ\n\n厳密に欲しい順というわけではないが、上3つが個人的に特に興味があるところ。で、lexicon.communityのターゲットはそこから外れていたっぽいという話。\n\nそれぞれどんなものなのか具体的に説明する。この類の方法論はatproto固有の話ではない部分も多いので既に議論が色々ありそうだが、浅学のため思いつきで好き勝手言っておく。\n\n### メタスキーマ\n\n様々なサービスが出てくると、サービス自体に関する情報を表す手段が欲しくなってくる。[`lexicon` lexicon](https://github.com/bluesky-social/atproto/tree/main/lexicons/com/atproto/lexicon)もその一種と言える。\n\n他にも、以下のようなものがありえるだろう。\n\n* 実装済APIの一覧取得API\n* appviewの利用条件・利用方法を示すAPI\n* データの(推奨)表示要件を示すrecord\n\n基本的にappviewに関する情報なのでAPIの形で表れることが多いと思うが、lexicon解決仕様によってNSID管理者のrepositoryというものが定まるようになったので、recordになることもあるかもしれない。\n\n### プロトコルユーティリティ\n\nサービスの中身にかかわらず、プロトコル仕様上あると便利なユーティリティというのもありえる。\n\n[`strongRef`](https://github.com/bluesky-social/atproto/blob/main/lexicons/com/atproto/repo/strongRef.json)や[`selfLabels`](https://github.com/bluesky-social/atproto/blob/main/lexicons/com/atproto/label/defs.json)等、既にある程度は[`atproto` lexicon](https://github.com/bluesky-social/atproto/tree/main/lexicons/com/atproto)で定義されているとは思うが、ここで新しいパターンが発掘されれば一番面白い。\n\n個人的には`strongRef`のcollection固定版が作れないか考えることが多い。[以前もそんな話を書いた](https://whtwnd.com/did:plc:qi6xg6zplzivyu7zrylxuugk/3kwmldx2b3l22)が、一般化には型パラメータが必要な気がしており、まだ解決していない。\n\nこのカテゴリはほぼobjectになるだろうが、enum stringとかもトップレベルで定義するのはあり。APIはメタスキーマ寄りな気はするが、まあ無くはないかも。\n\n### 共有データ\n\n複数サービスで共有するcollectionの話。全サービスで同じ情報を参照したい場合もあれば、サービス毎に異なるrecordを作るが串刺し検索などのAPIを提供したい場合もあるだろう。\n\n表示名やステータスや連絡先あたり。サブアカウント情報やatprotoレベルの[グローバルフォロー](https://github.com/orgs/lexicon-community/discussions/10)的なものもありえる。[Bluemoji](https://github.com/aendra-rininsland/bluemoji)も今回の分類の中ではここに該当しそう。\n\nとはいえあまり自明なものは思いつかないので、存在が周知されていても対立派閥ができたりしそう。\n\n### 確立済スキーマ\n\n既に一つのサービスとして完成したlexiconを共有する。bsky以外のサービスでプロフィールに[`getProfile`](https://github.com/bluesky-social/atproto/blob/main/lexicons/app/bsky/actor/getProfile.json)を使ったり、ブログサービスが[`whtwnd` lexicon](https://github.com/whtwnd/whitewind-blog/tree/main/lexicons/com/whtwnd/blog)を使ったりするのと似たようなもの。もちろん共有lexiconとして公開するならある程度は一般化してほしいが。使い方が明確な分、実践的と言える。\n\nlexicon.communityで[`smokesignal` lexicon](https://github.com/SmokeSignal-Events/lexicon/tree/main/events/smokesignal)を[引っ張ってきていたり](https://github.com/lexicon-community/lexicon/pull/23)、([recipes.blue](https://github.com/recipes-blue/recipes.blue)という競合が存在する)[recipe.exchange](https://recipe.exchange/)が[提案している](https://github.com/lexicon-community/lexicon/pull/28)のも、標準として定着させる意図がありそう。関係者間で合意が取れるのであれば特に言うべきことはない。\n\n### 汎用機能\n\nlikeのような、様々なサービスで利用可能なパターンを表すライブラリみたいなもの。実際にライブラリがセットで提供されることもありうる。冒頭で言及したbookmarkもここに分類されるだろう。\n\n何かしら定義してあれば悩んだ開発者のヒントとして有用だろうが、サービス毎の拡張がバリエーションが想定される場合は、ちゃんと標準化しようと思えばアプローチを考える必要がある。\n\nサービス横断的な共有データでないならば、lexicon検証やfirehose(jetstream)での抽出を考えると基本的にはrecordではなくobjectで定義するべきだろう。一方、APIに関しては多少のバリエーションがあっても同じNSIDを持っていた方が分かりやすいかもしれない。\n\n#### 包括的な定義\n\n考えうるユースケースを全てオプションとして表現可能にし、サービス毎に必要なサブセットを選択する。オプションフィールドがすごい数並ぶことになりそう。結局どのサブセットを使うかはそれぞれ異なるが、同じ意味のフィールドやAPIを同じ名前に統一できるという点がメリット。\n\n複数の仕様を後から統合しようとすると[CommonMark](https://commonmark.org/)みたいになりそうだが、先に決まっていて後から拡張する形ならまだなんとかなるかもしれない。それでも網羅的に定義しようとすると管理がとても大変だし、制約((文字数制限など)は表せないという欠点もあるが。\n\n#### 最低限の定義\n\nlexiconとして最低限提供するべき機能を定め、それ以外は独自拡張する。おそらく何かしらコアとなるAPIを決めて、そのAPIを成立させるために必要な情報等を定義していく形。\n\n例えば`countLikes` APIと`like`オブジェクトを定義する、とか。これくらいだったらエンドポイント共通化のために、汎用recordにしてNSIDをフィールドに持たせるという手もある。\\\n[`getLikes`](https://github.com/bluesky-social/atproto/blob/main/lexicons/app/bsky/feed/getLikes.json)まで定義しようとするとサービス固有の要素が入ってきて、エンドポイント共通化どころかlexicon上でも`unknown`になりそうだが。\n\nサービス毎の拡張はAPIなら(`output`に)独自フィールド追加だろうが、recordなら共通定義の外側に持つことが多くなりそう。\n\n#### 拡張前提の定義\n\nopen unionを使って独自要素を入れる場所を指定する。イメージとしては[`richtext`](https://github.com/bluesky-social/atproto/tree/main/lexicons/app/bsky/richtext)や[`embed`](https://github.com/bluesky-social/atproto/tree/main/lexicons/app/bsky/embed)。\n\n一番制御がきくので、巧くテンプレートを作ることができればメリットは多い。`union`は独自フィールドと違って名前が固定されており、`$type`必須なので(lexicon解決ができれば)内部構造が分かるという点もポイント。",
  "createdAt": "2025-02-10T09:30:56.368Z",
  "visibility": "public"
}