plumber を使ってみた
前回記事でリファクタリングの方針を立てたので、モノリスアプリケーションを作るところから始めてみる。
とりあえずリポジトリを作った。 開発コードは yoshimoto-samonji(義元左文字)。
技術選定
計算エンジンを Web UI から利用するモノリスアプリがゴールなら Shiny App 化すればいいのだが、 評価ステータス管理の実験をしていた当時のように、最終的には評価「事業」全体をアプリ化したい。 丁寧なドメインモデリングが必要になるので、フロントは始めから型を扱える TypeScript で実装しておきたい。
これを踏まえて、モノリスのバックエンドは plumber を使って構築することにした。 ドキュメントをざっと見てみたが、OpenAPI ドキュメントを生成してくれるようなので、フロント–バック境界は HTTP になる見込み。 ということは、フロントエンドに Next.js を使っておけば、Route handler を BFF に使える。 そうすると認証やセッション管理、画面向けのレスポンス整形を Next.js 側に寄せられるので、BFF を独立サービスとして立てる手間を抑えられそう。
ここまでを ADR にした。
plumber を使ってみる
hello, plumber
始めて触るので、plumber を使った最低限の実装を書いてみる。
とりあえずパッケージをセットアップし、 referenceを参考に実装してみた。
Plumber オブジェクトの $run() を実行する関数を作り、devtools::load_all() してから呼び出すと、指定したポートでサーバーが起動した:
> devtools::load_all(); start_server()
ℹ Loading legacy
Running plumber API at http://127.0.0.1:31501
Running swagger Docs at http://127.0.0.1:31501/__docs__/
クエリパラメータ msg に渡した文字列を利用したレスポンスを返す API ができた:
$ curl "http://127.0.0.1:31501/vpa/echo?msg=hello"
{"msg":["The message is: 'hello'"]}%
また、 生成された API ドキュメント。UI から動作確認できる/__docs__ にアクセスすることで Swagger UI も利用できた:

せっかく OpenAPI を利用できているので、将来的にはクライアントコードを生成するなどして契約駆動開発していきたい。
API スキーマをファイルに直接書き出すメソッドは無さそうだったが、getApiSpec() を使うと仕様をリストで取得できそうだったので、これを JSON にマッピングして書き出すことにした。
スキーマはいったん ignore しておいて、クライアントコード生成などは Next.js のセットアップをしてから整えることにした。
ちなみに生成されたスキーマを見ると、設定した覚えのないメタデータが既に入っていた:
{
"openapi": ["3.0.3"],
"info": {
"description": ["API Description"], <- このへんは設定した覚えがない
"title": ["API Title"],
"version": ["1.0.0"]
},
ドキュメントを見ると設定方法がちゃんと書いてあって、アノテーションか pr_set_api_spec() を使って設定できるらしい。
初めドキュメントの通りにアノテーションを足したつもりが、スキーマに反映されなかったが、このように Plumber オブジェクトを作っているファイル名を rp() の file 引数に指定することで反映されるようになった。
ドキュメントでは引数なしで動いているように見えたが、断りがない限り Plumber を作るファイル名は plumber.R になっている想定らしい。
コンテナ image 化
R ではライブラリ系の使われ方をする開発しかしたことなかったので、思考停止でパッケージアプローチを取る癖がついてしまっていたけど、
今回コンテナ用イメージを作る段になって、ランタイム系のイメージを作るにはパッケージアプローチは無駄が多すぎることに気づいた。
devtools のような開発系ツールや、その他パッケージ化に必要なメタデータもランタイムとしては完全に無駄なので、結局シンプルなスクリプト構成に移行した。
plumber がどうやって動作しているかを理解していなかったせいで、スクリプト形式に移行する過程でだいぶハマった。 それはそれで学ぶこともあったので、時間があったら別記事にする。
VPA を実行する image を作る
こちらのドキュメントを参考に
とりあえず vpa() を実行することにした。
データの保存場所は恐らく分かれると思うので、それぞれのデータは URL で受け取るようにした。
リクエストもレスポンスも、設計余地がけっこうありそうなので、まだ仮の実装にしてある。
将来的に API 仕様を変えることが明らかなので、予めバージョンを切っておいた。

バージョンを切った。
まず一つ API を作ってみた感じでは、見積もりの 5 倍くらいは時間がかかった気がする。 ただし、内訳としては plumber の使い方自体だったり、ビルドプロセスの調整などに手間取っていた。 API 自体の設計とかは全然していなかったので、今後は別のところに時間がかかるようになるはず。
学んだこと
改めて、使われ方に応じた構成をとることが大事。 型がないとスキーマ定義などがめちゃめちゃ面倒。
comments powered by Disqus