タスクベースUIとREST

この記事は[弁護士ドットコムアドベントカレンダー](/advent-calendar/2020/bengo4com) 20日目の記事です。
昨日の記事は@amashigeseijiさんの[タスクベースUIとCQRS](/amashigeseiji/items/3ad45bc08d15ecf7b0a7)でした。

# はじめに

@amashigeseijiさんが上記記事投稿後、以下のようなつぶやきをしていました。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">タスクベースUIってハイパーメディアコントロールじゃないの?とかの話題もあるけど、時間かけなきゃ書けない...</p>&mdash; 天重誠二 (@tenjuu99) <a href="https://twitter.com/tenjuu99/status/1340052681574866945?ref_src=twsrc%5Etfw">December 18, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

本記事では@amashigeseijiさんに変わり、上記の「ハイパーメディアコントロールじゃないの？」という点について簡単に解説していきます。
ですので、上記を先に読んであることを前提にしている部分も多少あります。面白いので先に読んでからが良いと思います。

# Task-Based UI

以下は[CQRS Documents by Greg Young](https://cqrs.files.wordpress.com/2010/11/cqrs_documents.pdf)のFigure7を引用したTask-Based UIの例です。
テーブルの各行は `Inventory Item` というものを表しており、 それぞれ`Name`、`Supplier`、`Active` という情報を持っており、 `Active` の列にチェックマークが入っている `Inventory Item` に関しては、4列目に`Deactivate` というLinkを持っています。

![スクリーンショット 2020-12-19 12.06.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/129649/99358a71-51df-8f32-12c8-c119829a95d0.png)

ユーザがある`Inventory Item`の`Deactivate` Linkをたどると、以下のような画面に遷移します。

![スクリーンショット 2020-12-19 12.06.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/129649/68287c4d-d0dc-d61c-0387-2011be4922ef.png)

ユーザは、アイテムを無効にする理由についてのコメントを求められます。


[原文](https://cqrs.files.wordpress.com/2010/11/cqrs_documents.pdf)にもありますが、この一連の操作において、ユーザの意図は非常に明確であることがわかり、ソフトウェアの誘導によって実現されています。

このソフトウェアによる誘導が、Webの世界ではハイパーメディアコントロールと呼ばれるもので、RESTにおいて非常に重要な要素[^hateoas]です。

[^hateoas]: 4つのインターフェース制約の内の1つハイパーメディア制約になります。Hypermedia as the Engine of Application State (HATEOAS)

# ハイパーメディアコントロール

ハイパーメディアコントロールとは、アプリケーション状態（クライアントとサーバーのやりとりでクライアントがどこに位置しているかという状態）をハイパーメディアによって制御することを表します。クライアントの位置は、リクエストのURLによって表されます。サーバーはリンクによってクライアントが次に行うべき行動を示し、クライアントはそのリンクによって状態遷移を行います。

先の`Inventory Item`を例にしてみましょう。
アプリケーション状態の遷移図（Application state diagram;ASD）を以下に示します[^asd]。四角が状態、矢印が遷移を表します。
ASD上の`Figure7`、`Figure8`は引用した画像に対応しています。

[^asd]: https://github.com/koriym/app-state-diagram で [ALPS](http://alps.io/) から生成しました

![スクリーンショット 2020-12-19 17.11.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/129649/3544cacd-d6d7-7e04-27e8-55399a9322a7.png)

ASDで表現したアプリケーションがWeb上に存在することを考えてみます。
ユーザがブラウザに`Figure7`を指すURLを入力します。すると、サーバーからは以下のようなページが返されます。

![スクリーンショット 2020-12-19 12.06.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/129649/99358a71-51df-8f32-12c8-c119829a95d0.png)

この状態からユーザができることは、サーバーから示された`Deactivate`のリンクをたどることです。
ユーザが`Deactivate`のリンクをクリックすると、以下のページが返されます。

![スクリーンショット 2020-12-19 12.06.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/129649/68287c4d-d0dc-d61c-0387-2011be4922ef.png)

サーバーから示されたリンクをクリックすることで、アプリケーション状態が`Figure7`から`Figure8`に遷移しました。ハイパーメディアコントロールによるものです🎉

`Figure8`の状態からユーザができることは以下の2点です。

1. `Cancel`をクリックして`Figure7`の状態に遷移する
2. コメントを入力し、`Deactivate`をクリックして次の状態に遷移する

ASDでは、2の遷移先を`Figure7`にしていますが、いずれにせよ遷移先はサーバーによって示されているという点がポイントです。
また、2の遷移によってサーバー上のリソース状態も変化しているでしょう。
具体的にどういった変化があったのかはユーザにはわかりませんが、最初にアクセスした`Figure7`の状態と2の後の`Figure7`の状態は異なるのではないでしょうか。

# APIとして提供する

上記の体験をAPIで提供するにはどうしたら良いでしょうか。
サーバーからリンクやフォームを渡し、ユーザはそれに従って状態遷移をするという基本的な考え方は変わりません。

`application/json`がAPIのレスポンス形式としては一般的ですが、`application/json`はメディアタイプとしてハイパーメディアをサポートしていないので、クライアントはどこにURLが存在するのかわかりません。
`application/hal+json`[^hal]や`application/vnd.siren+json`[^siren]などを利用するのが良いでしょう。

[^hal]: http://stateless.co/hal_specification.html
[^siren]: https://github.com/kevinswiber/siren

以下のような記事もあります。
[JSONでフォームを表現する](/tkawa/items/2414eb954cb700c3a280)
ハイパーメディアをサポートしているフレームワークもたくさんあります。

- [BEAR.Sunday](https://bearsunday.github.io/index.html)[^sunday]
    - [RESTアプリケーション設計実装手順](https://qiita.com/koriym/items/cb6efd0ab2fb8751f9e9)
- [Spring HATEOAS](https://spring.io/projects/spring-hateoas)
    - [Spring HATEOAS - Reference Documentation](https://docs.spring.io/spring-hateoas/docs/current/reference/html/#reference)
- [Laravel HATEOAS](https://github.com/gdebrauwer/laravel-hateoas)

[^sunday]: A resource-oriented application framework

# Gregさんの考えの変化

[タスクベースUIとCQRS](/amashigeseiji/items/3ad45bc08d15ecf7b0a7)にも注記があるとおり、
REST=CRUDと理解していたのか2009年ごろはかなりRESTに批判的でした。当時のツイートを一部引用します。

<blockquote class="twitter-tweet"><p lang="en" dir="ltr">watching the REST group discuss CQS is extremely painful.</p>&mdash; Greg Young (@gregyoung) <a href="https://twitter.com/gregyoung/status/1923868384?ref_src=twsrc%5Etfw">May 26, 2009</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

しかし、翌年にはRESTはCQRSだ（超意訳）とtweetしており、REST=CRUDという考えではなくなったように感じます。

<blockquote class="twitter-tweet"><p lang="en" dir="ltr">I used to think RESTand a CQRS based architecture were at odds with each other. This is not true, REST done right is a `CQRS` architecture</p>&mdash; Greg Young (@gregyoung) <a href="https://twitter.com/gregyoung/status/14040334079?ref_src=twsrc%5Etfw">May 15, 2010</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

# DDDとALPS

うっかりDDDを持ち出しました。朝5時とかにならないよにさらっと書きます。

@amashigeseijiさんの記事にもあるとおり、DDDがタスクベースUIを必要とするのは、操作の意味づけのためです。
ALPSを用いることによって、APIがクライアントに対してセマンティックをシェアできるようになります。
APIレスポンスの内容だけでなく遷移に対してセマンティックを付与することも可能です。

[弁護士ドットコムアドベントカレンダー](/advent-calendar/2020/bengo4com) 10日目の@koriymさんの記事が詳しいです。
[メディアタイプとALPSプロファイル](/koriym/items/2e928efb2167d559052e)

# まとめ

[CQRS Documents by Greg Young](https://cqrs.files.wordpress.com/2010/11/cqrs_documents.pdf)のTask-Based UIを例に、ハイパーメディアコントロールについて解説しました。
ハイパーメディアコントロールそのものは、特に目新しいものではなかったと思います。普段当たり前のように体験しているものですからね。
ですが、APIになったとたんハイパーメディアコントロールは失われてしまいがちです。
APIを設計する際にも状態遷移を中心に考え、サーバーからリンクやフォームを示しクライアントの状態遷移を制御することによって、Web本来の拡張性の高いAPIをデザインできるのではないでしょうか。

[RESTful Web APIs](http://restfulwebapis.com/)という本の読書会を弁護士ドットコム主催で2019年10月から開催しています。
BEAR.Sundayの作者である@koriymさん、RESTエキスパートの@tkawaさんご協力のもと、現在はオンラインで月1回のペースで開催しています。
年明け1月は、総集編になります。RESTについて理解を深めたい方はぜひご参加ください。
日程は現在調整中ですが、私のツイッターで日程決まり次第アナウンスします。https://twitter.com/TSUCHIYA_Naoki

明日は、@komtakiさんの「狼少年にならないフロントエンドエラー監視システムの工夫」です。お楽しみに！


# 参考文献

Richardson, Leonard; Amundsen, Mike; Ruby, Sam. RESTful Web APIs: Services for a Changing World . O'Reilly Media. Kindle 版. 
https://magazine.rubyist.net/articles/0050/0050-Hypermedia.html
https://docs.microsoft.com/en-us/previous-versions/ms997506(v=msdn.10)
https://www.slideshare.net/tkawa1/truerest20191214
https://speakerdeck.com/koriym/rest-6-plus-4falsezhi-yue