メディアタイプとALPSプロファイル

こんにちは。
弁護士ドットコムの[テクニカルアドバイザー](https://hrmos.co/pages/bengo4/jobs/0009602)の@koriymです。
この記事は [弁護士ドットコム Advent Calendar 2020](https://qiita.com/advent-calendar/2020/bengo4com)の10日目の記事です。

昨日はSREの@t2ynkmrさんの[「ECSのデプロイ方法を見直して任意のタイミングでコミットハッシュをタグに使ったイメージをデプロイできるようにした」](https://qiita.com/t2ynkmr/items/606a7bf94e353ffd8784)でした。（すごい事してます！）

# メディアタイプとALPSプロファイル

この記事では初めにメディアタイプの中でも**ドメイン固有のメディアタイプ**に注目します。次に**プロファイルリンク**を使ってアプリケーションセマンティクスを与える方法や、そのプロファイルを情報設計（インフォメーションアーキテクチャ IA）のツールとして使う事を説明します。

[^as]: アプリケーションで使う語句に意味を明示的に与える事です。

記事を通じてインターネットの初期から存在したWebにセマンティクスを与える（対象とするコンテントの意味をマシンに教える）仕組みを、次世代のAPI開発に応用する方法を考察します。

## メディアタイプ

>Media Typeは、ファイルやインターネット上で転送されるコンテンツの形式を表現する識別子である。 (wikipediaより）

Content Type[^ct]やMIME Type[^mt]とも呼ばれますが、現在、Media Typeが正しい呼び方です。
[^ct]: Content TypeはHTTPヘッダーでのメディアタイプの指定する時に使われます。
[^mt]: MIME TypeはMedia Typeの以前の呼び方 https://www.iana.org/assignments/media-types/media-types.xhtml

メディアタイプはIANA[^iana] が管理していて、一覧は [メディアタイプレジストリ](https://www.iana.org/assignments/media-types/media-types.xhtml)にリストされています。

[^iana]: Internet Assigned Numbers Authority https://www.iana.org

例えば最も有名なメディアタイプに [text/html](https://www.iana.org/assignments/media-types/text/html) があります。IANAレジストリでは、そのメディアタイプの登録者[^1] 、このメディタイプの利用するアプリケーションは何か[^2]、詳細仕様は[http://www.w3.org/TR/html](http://www.w3.org/TR/html) で定義されている といったことが分かります。

[^1]: ロビン・ベルジョンさん ([@robinberjon](https://twitter.com/robinberjon)) W3CでHTML5の仕様策定者。
[^2]: Webブラウザ、オーサリングツール、サーチエンジン

### ドメイン固有のメディアタイプ

`application/json`がどんな内容でも扱えるのに対して、`application/problem+json`は[RFC7807](https://tools.ietf.org/html/rfc7807) で定義されたクライアントにエラー情報を伝えるためのメディアタイプです。HTTPのエラーコード以上の情報をJSONオブジェクトで伝えることができます。標準を使えば、[各社各サービスでエラーを発明](https://qiita.com/suin/items/f7ac4de914e9f3f35884)する必要はありません。多くのメディアタイプは拡張性があり、アプリケーション固有のエラー情報も既存のメディアタイプに付加できます。

`text/html`や`application/json`はあらゆる事に関し記述する事ができる**ジェネリックメディアタイプ**です。それに対して`application/problem+json`のように、特定ドメインの利用のみに使うメディアタイプを**ドメイン固有のメディアタイプ**と呼びます。

以下は`application/problem+json`の例です。

```
HTTP/1.1 400 Bad Request
Content-Type: application/problem+json

{
 "type": "https://example.net/validation-error",
 "title": "Your request parameters didn't validate.",
 "invalid-params": [{
   "name": "age",
   "reason": "must be a positive integer"
  }]
}
```

上記エラーメッセージの`type`や`title`は、単に人間が読んで理解できるだけでなく`application/problem+json`を理解するクライアントにとってはそれが特別な意味を持つ語として扱うことができます。

### vendor固有のメディアタイプ

 [メディアタイプレジストリ](https://www.iana.org/assignments/media-types/media-types.xhtml)には`vnd`ツリープリフィックスが特定のvendorのためのメディアタイプもあります。
 
 * [application/vnd.google-earth.kml+xml](https://www.iana.org/assignments/media-types/application/vnd.google-earth.kml+xml) (Google Earth)
 * [application/vnd.ms-excel](https://www.iana.org/assignments/media-types/application/vnd.ms-excel) (MS Excel)
   
他にも個人/実験のための`prs` や未登録を表す`x`などのツリープリフィックスがあります。

メディアタイプによってリソース表現で表されるセマンティクスを理解でき、使われる言葉と正確な意味を結びつけることができます。アプリケーションがメディアタイプによってコンテンツをどのように取り扱うかを決定することができます。

## プロファイル

では私たちが作成するアプリケーションやAPIも、利用者のためにセマンティクスを定義したメディアタイプを用意すればいいでしょうか？ 例えば`application/vnd.bengo4.lawer-list`のように？ [^gh-mt] もっといい方法があります。それが**プロファイル**[^profile]です。

[^gh-mt]: GitHub APIはこれを行っています。https://docs.github.com/en/free-pro-team@latest/rest/overview/api-previews これらのカスタムメディアタイプはIANAに登録されてもいません。

[^profile]: RFC6509 The 'profile' Link Relation Type https://tools.ietf.org/html/rfc6906

**プロファイルリンクリレーション**は既存のメディアタイプにアプリケーションのための追加情報を付与するための仕組みです。例えば既存のメディアタイプ`text/html`や`application/json`に対して、追加のアプリケーションセマンティクス[^as]を指定します。

アプリケーションやAPIで使用する言葉の意味をアプリケーションセマンティクスとして、HTTPリクエスト・レスポンスの振る舞いをプロトコルセマンティクスとして定義するとアプリケーションやAPIのマニュアルになります。

HTTPヘッダーにプロファイルをリンクします。

```
Content-Type: application/json
Link: <https://dev.twitter.com/docs>; rel="profile"
```
 
JSONレスポンスの内容はprofileリンクで表されたURLを見ればその意味や使い方を知ることができます。これはREST制約の４つの統一インターフェイス制約（(a) リソースの識別、(b) 表現によるリソースの操作、(c) 自己記述的メッセージ、(d) アプリケーション状態のエンジンとしてのハイパーメディア）の中の１つ、**自己記述的メッセージ** の特徴です。[^not_crud] 

[^not_crud]: [広く誤解されてますが](https://qiita.com/search?q=%E7%B5%B1%E4%B8%80%E3%82%A4%E3%83%B3%E3%82%BF%E3%83%BC%E3%83%95%E3%82%A7%E3%82%A4%E3%82%B9)、**統一インターフェイス制約**はHTTPメソッドとCRUDのマッピングではありません。https://www.infoq.com/jp/articles/subbu-allamaraju-rest/

プロファイルのフォーマットに規定はなく、人間がそのアプリケーションのアプリケーションセマンティクスとプロトコルセマンティクスを理解することができればOKです。HTMLやMD、あるいはプレーンテキストで記述してあっても構いません。

## ALPS

プロファイルを特定フォーマットで記述して人間にも読む事ができるだけでなく、マシンにも解析可能なものにしようとするのが[ALPS](http://alps.io) (Application-Level Profile Semantics)です。

ALPSフォーマットは、解決策ではなく問題を記述することに集中できるように設計されています。URLやステータスコード、HTTPメソッドのようなものはドキュメントの一部ではありません。その代わりに、アクション(read, write, submit, approve など)やデータのプロパティ(familyName, givenName など)が含まれています。

`userName` を説明する「userNameはユーザーの名前です」という自然言語の文章の意味をマシンが理解することはできませんが、`userName `というのが何か特別な意味のある語で、それが`User`リソースに含まれ、`user-registration`アクションを行うときに必要な語であるということは定義することができます。

## セマンティクス

アプリケーションで使う語を定義するのはALPSの重要な役割です。

### 意味
	
```
{"id": "articleBody", "type": "semantic"}
```
これは ALPSアルプスドキュメントの一部で"articleBody" という語句を定義しています。この段階では`articleBody`の意味は定義されていません。アプリケーションには`articleBody`という語があるという事を定義しているだけです。[^alps-semantic]

[^alps-semantic]: 実際にはtypeのデフォルトはsemanticなのでtype:semanticは省略可能です。

意味を記述するのは`doc`を使います。

```
 {"id": "articleBody", "type": "semantic", "doc": {"value": "記事の本文"}}
```

自然言語で説明するより、もっといいのはボキャブラリサイトにリンクする事です。`def`を使います。

```
 {"id": "articleBody", "type": "semantic", "def": "https://schema.org/articleBody"}
```

[schema.org](https://schema.org/)の`articleBody`にリンクすることで意味を定義しました。このようなボキャブラリのサイトは他に[dublincore.org](https://dublincore.org/)、[activity stream](https://activitystrea.ms/) があります。

語句を定義した事で、このアプリケーションで`articleBody`を使うときに説明はもう必要ありません。APIの定義で繰り返し同じ文章で説明する必要はなくなります。ボキャブラリのサイトを利用することで表記ゆれもなくなり、文化的によりニュートラルな言葉を使うことができます。[^term]

[^term]: 例えばschema.orgではPersonのプロパティにfirstNameではなくgivenNameが使われています。https://schema.org/givenName

この`articleBody`など定義した語句を**セマンティックディスクリプタ**（意味的記述子）と呼びます。ALPSドキュメントはこのセマンティックディスクリプタ定義の集合です。

### 分類

セマンティックディスクリプタは他のセマンティックディスクリプタを含む事ができます。

```
{
  "id": "BlogPosting", "type": "semantic", "def": "https://schema.org/BlogPosting",
  "descriptor": [
    {"href": "#articleBody"},
    {"href": "#dateCreated"},
  ],
  {"id": "articleBody", "type": "semantic", "def": "https://schema.org/articleBody"},
  {"id": "dateCreated", "type": "semantic", "def": "http://schema.org/dateCreated"}
},
```

上記の例は`BlogPosting`ブログ記事が`articleBody`ブログ本文と`dateCreated`作成日付を含んでいることを表します。`#`を使ってインラインリンクしています。多くの場合、リソース表現（webページやAPI）は特定のセマンティックディスクリプタの集合です。

### リンク

リソース間の遷移は以下の３つのタイプのいづれかで表すことができます。

* `safe` リソース状態が変わらない安全な遷移です。HTTPだとGETです。
* `unsafe` リソース状態が変更され冪等性がない遷移です。HTTPではPOSTです。
* `idempotent` リソース状態が変更され冪等性がある遷移です。HTTPではPUTもしくはDELETEです。

以下は`BlogPost`への安全な遷移を表しています。遷移先は`rt`で示します。

```
{"id": "goBlogPost", "type": "safe", "rt": "#BlogPost"},
```

遷移に何かセマンティクスが必要な場合には同じように`descriptor`で含みます。

```
{"id": "goBlogPost", "type": "safe", "rt": "#BlogPost", 
  "descriptor": [{"href": "#id"}]
}
```
これらのディスクリプタの集合を配列として以下のようになフォーマットでまとめるとALPSドキュメントになります。

```json
{
  "alps": {
    "title": "...",
    "doc": {"value": "..."},
    "descriptor": [
      {"id": "descriptor1", ...},
      {"id": "descriptor2", ...},
      {"id": "descriptor3", ...}
    ]
  }
}
```
下記の例もご覧ください。

* [blog](https://github.com/koriym/app-state-diagram/blob/master/docs/blog/profile.json)
* [todomvc](https://github.com/koriym/app-state-diagram/blob/master/docs/todomvc/profile.json)

作成したALPSドキュメントは`asd`を使ってアプリケーションステートダイアグラムにしたり、unifiedを使ってOPEN APIなどIDLに変換することができます。

JSONの入力に不安や不満がありますか？ JSON Schemaを使った入力は補完もバリデーションも効くのでスムーズです。IntelliJやVS CodeなどJSON Schemaをサポートしたエディタをお使いください。

次のスキーマを設定してALPSドキュメントを始めてください。XMLやYAMLからの変換ツールもあります。

https://raw.githubusercontent.com/alps-io/spec/master/schemas/alps.json



## ASD (Application State Diagram)


### アプリケーション状態

アプリケーション状態とは、クライアントとサーバーのやりとりでクライアントがどこに位置しているかという状態です。例えばECサイトでは商品を見ているのか、注文して支払いにしているのか、といったURLとして反映されるクライアントのリクエスト位置の情報です。サーバーから与えられるリンクによってアプリケーション状態が遷移するのがRESTです。[^rest] [^hateoas]

[^rest]: HTMLのAタグやFORMタグでURLが与えられるHTMLがその典型です。クライアントはURLを組み立てずサーバーから示されたリンクをたどるだけです。

[^hateoas]: REST制約の中の4つの統一インターフェイス制約の1つ、**アプリケーション状態のエンジンとしてのハイパーメディア**でもこの用語が使われています。 HATEOAS([Hypermedia as the Engine of Application State](https://en.wikipedia.org/wiki/HATEOAS))とも呼ばれます。
### ダイアグラム

[asd](https://github.com/koriym/app-state-diagram)ツールはALPSドキュメントから**アプリケーション状態遷移図**を描画します。ダイアグラムはSVGでレンダリングされ、四角で表されたのがアプリケーションステート、矢印で表されているのがその遷移です。クリックしてその詳細を見る事ができます。

[![ダイアグラム](https://koriym.github.io/app-state-diagram/blog/profile.svg)](https://koriym.github.io/app-state-diagram/blog/profile.svg)
  
皆さんはどのようなツールを使ってWebサイトやWebサービスの情報設計を行なっていますか？ どのようにアプリケーションが扱うコンテンツの名前を決めたり、意味を定義したり、用語を統一していますか？ コンテンツの内包関係や関係性はどうでしょうか。

### ALPS + ASD

ALPS + ASDはサイトからUIやデザイン要素を除いた状態でアプリケーションの情報スケルトンだけを取り出し視覚化したものです。単純な図ですが、全てのアプリケーションセマンティクスとプロトコルセマンティクスが含まれています。

遷移を含む全ての情報要素にIDが振られたこの図を使い、non-techの人も含めてサイトの情報設計を共有することができます。定義された語句はプロダクトオーナー、サーバーサイドエンジニア、クライアントサイドエンジニア、デザイナーが理解共有するのに役立ちますし、それぞれの言語で共通の語句を利用することができます[^common]。
[^common]: CSS、API、サーバーサイド言語やJSでの変数、クラス名、メソッド名、DBスキーマなど

GUIツールと違い、図もJSONから描画するのでgit管理して履歴や差分を残すことができます。誰がどの語句や繊維をいつ追加したか記録することができます。

ALPSは特定のメディアタイプに囚われない、RESTアプリケーション設計のための問題を記述する言語です。HTTPやOpenAPIといった実装の上位に位置します。この段階ではURLやHTTPステータスコード、スキーマによる制約といった実装のための制約はまだ出現していません。

[^alps-objectvie]: ALPSの目的は[Web Ontology Language (OWL)](https://kanzaki.com/docs/sw/webont-owl.html)と似ていますが、対象をより狭くフォーカスして定義されています。OWLが多くの異なる種類の知識の構造を表現できるのに対し、ALPSはセマンティクスがアプリケーション内でどのように使われるかに焦点を当てています。

[ドキュメントサイトのデモ](https://koriym.github.io/app-state-diagram/index.html)を試してみてください。

## ALPS Unified

設計言語を実装の詳細にブリッジするのがALPS Unifiedです。

[ALPS Unified](https://github.com/mamund/alps-unified)はALPSドキュメントを各種IDL (Interface definition language)に変換します。現在サポートしているのはOpenAPI, SDL, Proto3, AsyncAPI, WSDLです。

Amazon CDKをサポートする[CDK Alps Spec Rest Api](https://github.com/mmuller88/cdk-alps-spec-rest-api)も開発が続けられています。

## 弁護士ドットコムでの取り組み

現在、弁護士ドットコムでBUSINESS LAWYERS（ビジネスロイヤーズ）というサービスでALPSを使った情報設計に取り組んでいます。得られたフィードバックは後述するAOOHでALPSコミュニティに伝えALPSプロジェクトにも役立っています。

弁護士ドットコムは継続してきたビジネスとそのソフトウエア基盤がありますが、週に一度のTech Focus Day (TFD)では既存コードのリファクタリングや実験的な事でも可能な新しい取り組みを行なっています。ALPSもTFDで取り組み始めました。

##  ALPS Open Office Hours (AOOH)

今年の10月からAOOHという1h程度のオンラインミーティングが週一で開催されています。
参加は少人数ですが、主催の[@mamund](https://twitter.com/mamund)さんは多くの登壇経験や著作のある世界でも最高のAPIコンサルタントの一人です。そういった一流の方とアイデアを出し合い、議論し、プロジェクトを前に進めていくことはエキサイティングです。

私の英語はBad Englishですが、[オープンソースにはもっとBad Englishが必要です。](https://ostatic.com/blog/more-bad-english-please) プロジェクトにコントリビューターとして関わりたいと考え、AOOHに参加を続けています。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3940/8efcf2c6-9e82-436e-80ea-e786d1f44331.png" width="400">

[http://mamund.site44.com/alps/](http://mamund.site44.com/alps/)

## RWABook

[RESTful Web APIs](http://restfulwebapis.com/index.html) は2013年に発売された本ですが、今日でも本格的なREST API (Hypermedia API)を設計するのに必要な読む価値のある良書です。リソース表現やハイパーメディアなどのWebの基礎技術を改めて紹介した後に、ドメイン固有設計や、コレクションパターン、ハイパーメディア設計を学び、REST APIの設計手法を学びます。付録のAPI Designer's Guide to the Fielding Dissertation (API設計者のためのREST論文）も読み応えがあります。

まずは目次をご覧になってください。
http://restfulwebapis.com/chapters.html

## RWABook読書会について

改めて読書会をしようとしたモチベーションになったのが [RESTlessnessに打ち勝つ](https://www.infoq.com/jp/articles/overcoming-restlessness/) という記事です。"ソフトウェアエンジニア産業は，成熟したRESTエコシステムを基盤として，新たなプロトコルの技術的長所を探求する手段を模索するべき" との意見に共鳴します。

読書会は弁護士ドットコム主催で RESTful WEB APIs読書会として2019年10月から開催しています。当初はオフライン開催で参加者に弁護士ドットコムからお寿司＋飲み物を提供していただいていました。現在はオンラインで月1回の開催で、毎回、国内最高のRESTエキスパートの[@tkawa](https://twitter.com/tkawa)さんにご参加いただいています。

<image src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3940/68c6783c-8349-80c9-a48b-9ade22e65bb7.png">

本書は英語版しかありませんが、DeepLと@tkawaさんの助けを借り一人で読み進めるよりグッと楽しく進めてるのではないかと思います。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3940/b489ecea-f615-8537-aa11-b7c8af0d7db5.png" width="350">


### 来年もよろしくお願いします

来年最初の1回目はこれまでの総集編を考えています。今までの一年はなんだったのかというくらいきちんとまとめてお話ししたいと考えてますので、ちょっと休んでた方も、初めての方も是非ご参加ください！ 予定は運営担当の[@TSUCHIYA_Naoki](https://twitter.com/TSUCHIYA_Naoki)さんや、[compass](https://bengo4.connpass.com/)から発表されます。

明日は @happylifetaka さんの記事です。お楽しみに!

## 参考リンク
 
* [RESTの力、RESTの制約](http://koriym.github.io/blog/2019/12/30/the-power-of-rest/)
* [ALPS Unified Utility](https://mamund.substack.com/p/alps-unified-utility?r=4hpyc&utm_campaign=post&utm_medium=web&utm_source=twitter)
* [RESTアプリケーション設計実装手順](https://qiita.com/koriym/items/cb6efd0ab2fb8751f9e9)
* [alps-asd](https://alps-asd.github.io/manuals/1.0/ja/)