メディアマックスジャパン株式会社では、Kotlin でのサーバーサイド開発にあたって Jooby という軽量 Web フレームワークを採用しました。Jooby はあまり知られていないフレームワークなので、どういった特徴を持ったフレームワークなのか、なぜ採用したかについてお話させていただこうと思います。
Jooby の紹介
冒頭の画像は Jooby 公式サイトのスクリーンショットです。Kotlin のサンプルコードがトップページに表示されていることからも Kotlin 対応に力を入れていることがわかります。
まずは Jooby がどういった特徴を持つのかをご紹介します。
Kotlin 対応
この記事は Kotlin Advent Calendar 2017 で紹介させてもらっているので最重要のフィーチャーですね。
Kotlin は Java との相互運用性を重視している言語なのでフレームワークがあえて Kotlin 対応する必要はないのですが、Kotlin で書きやすいのはやっぱり嬉しいですよね。type-safe builder を利用して実に Kotlin らしい DSL でルーティングをはじめとする設定が記述できます。
フレームワークの API に NonNull アノーテーションが付けられており Null Safety が保証されているのも Kotlin ユーザーにとって嬉しいポイントです。
型安全性
プログラミング言語自体が静的型付けであってもフレームワーク全体で見ると設定ファイルやテンプレートなど型安全性が保証されない箇所が出てきます。Jooby の特筆すべき点に型安全性を非常に重視したフレームワークであることが挙げられます。具体的には以下の点が大きいところになります。
- Java/Kotlin DSL でルーティングをはじめとする設定を記述する
- リフレクションやアノーテーションの利用は最小限に抑えられている
- Kotlin 利用時は Null Safety が保証されている
モダン Web フレームワークの特徴
何をもってモダンと言うべきかは議論が別れる点だとは思いますが、Rails 以降のフレームワークが持つようなフィーチャーは一通り備えています。
- Servlet API ではなくシンプルな request/response API
- Session ストレージのデフォルトは Cookie で状態を持たずスケールアウトが簡単
- dev, prod のような環境の仕組み
- unit test, integration test のための Mock API
- スタックトレースやエラー発生箇所を確認できる開発時のエラー画面。実行時エラーが発生したときブレークポイントを設定して再実行しなくても一通りの情報が得られると開発がサクサク進みます。
- live reload 対応。コード変更時にブラウザをリロードする必要すらありません。
- 非同期処理に対応。マイクロサービス構成を取るとき並列に API を呼び出せるとレスポンス速度を大きく向上させます
- 実行可能な single file jar へのビルド。gradle / maven プラグインももちろん完備。
- http2 server push, web sockets, server-sent events に対応。
- HTTP 実装は Netty, Jetty, Undertow から選択可能
パフォーマンス
TechEmpower の Web Framework Benchmark (Round 14) が示す通り Java 環境で10位以内のスループット、レスポンス速度を叩き出しています。パフォーマンスについてはそれ以上言及する必要はないかと思います。
モジュールシステム
Jooby のアーキテクチャも外せないポイントです。軽量ウェブフレームワークとしてコアは小さく留めつつ、モジュールという仕組みで様々な機能をプラグインできるようになっています。単にプラグインの仕組みを備えているだけでなく、標準で様々なモジュールが提供されているので要件に応じたスタックを構成することができます。
モジュールのインターフェースはとても小さく configure メソッドを実装するだけで、依存関係が最小限に絞られているのも嬉しいところ。お手本にしたくなるような練られたアーキテクチャです。
選定基準
技術を選択するにあたって重要になるのはその選定基準です。弊社では全エンジニア間で議論・合意の上で特に重要な基準として以下を検討することにしました。
厳密に言えば、フレームワークと言語のセットで選定を行ったのですが、本記事は Kotlin Advent Calendar 2017 の記事でもあるので、Kotlin 自体の採用理由については別記事であらためて紹介したいと思います。
型安全性
弊社ではこれまで Ruby on Rails をメインに利用してきました。使い捨ての小さなアプリケーションであれば Rails は今でも良い選択肢だと思います。
ですが、ある程度の複雑さを持つようになってくると動的型付け言語のマイナス面が無視できなくなります。リフレクション機能を多用している Rails では grep も信頼できず、メソッド名の変更も躊躇してしまう場面が出てきます。
また、アプリケーションの寿命は想定したより長くなるもので Rails 本体や利用ライブラリのアップデートも頭を悩ませる問題です。すべてをカバーするテストは現実的ではないですし、そもそも静的な型があればコンパイラがチェックできるようなことまでユニットテストで書くのは本末転倒です。
設定やテンプレート等を含めたフレームワーク全体での型安全性は、弊社の全エンジニアが即座に必要だと同意した選定基準でした。
Jooby は後述する JOOQ, Rocker と組み合わせることで、ほぼすべての領域で型安全性を保証してくれます。
パフォーマンス
パフォーマンスも Rails では苦労する点です。
アプリケーションの複雑さにもよりますが ActiveRecord を使わず直接 SQL を実行したり、テンプレートのレンダリング結果をフラグメントキャッシュで細かく管理したり、様々な工夫をこらしてもサーバーサイドの処理で 200 msec 以内を保証するのがやっとというところでしょうか。
選定にあたっては、素直に書いたコードで十分なスループットとレスポンス速度が得られることが必須条件でした。
Jooby のパフォーマンスは上記の通り Java フレームワークで最速を争う水準です。
エコシステム
一般的に言えば、エコシステムが充実しているかどうかは最も重要な選定基準です。エコシステムが充実しているプロダクトは下記のアドバンテージを持ちます。
- 多様なユースケースを満たしている
- エッジケースにいたるまでバグが発見され修正されている
- 周辺ライブラリが揃っている
- ドキュメントやノウハウが充実している
- ツールが充実している
- ブラウザやプロトコルをはじめとする環境変化にも素早く追随する
エコシステムの観点で言えば Jooby は失格と言わざるをえません。公式ドキュメント以外で Jooby について書かれた文書は Medium で数件見つかる程度です。
ですが、公式ドキュメントだけで必要十分な情報が得られること、たとえプロジェクトが休止状態になったとしても自分達でフォークして保守できるソースコードの規模とわかりやすさから、エコシステムの小ささは受容可能だと判断しました。
疎結合なアーキテクチャ
疎結合であることも Rails での経験から導きだされた選定基準でした。
Rails はフルスタックフレームワークです。それも Rails 登場の前後でフルスタックの意味が変わってしまうぐらいにフレームワークの領域を広げました。プロジェクトの scaffold にはじまり、ユニットテスト、コード生成、データベースマイグレーション、CLI ツールなど開発中に必要なツールまでをフレームワークが提供してくれます。
その一方で、密結合でモノリシックであり、すべてを提供してくれるがゆえに、一度その上でアプリケーションを構築してしまうと Rails の提供してくれるレールから外れることは困難です。
わかりやすい例で言えば Rails 3.1 から導入された sprockets が挙げられます。 当時のフロントエンド開発に必要なすべてを提供してくれました。coffeescript/scss などのトランスパイラ、リクエスト数を減らすための bundle、ブラウザキャッシュを最大限に活用するための hash digest 付与。加えて jquery や bootstrap も sprockets の規約にしたがって gem で提供されてました。
その後のフロントエンド環境の変化スピードは皆さんがご存知の通りで、わずか3ヶ月前の情報が古びてしまうようなペースで発展を続けています。カンブリア爆発を思い起こさせるように様々なライブラリやツールが生まれ消えていきました。backbone.js, Ember.js, CoffeeScript, bower, grunt, gulp, browserify など挙げるとキリがありません。
ですが、Rails の sprockets や turbolinks に依存してしまったアプリケーションを今のフロントエンド環境に置き換えるのは至難の業です。capistrano による build, deploy 部分から手を入れていく必要があります。たしかに最新の Rails 5.1 では webpacker という仕組みでフロントエンドで発展したツールを取り込みました。しかし、それに依存してしまうと、将来 ES module が普及して webpack が廃れたときに同じ問題に遭遇することになります。
Jooby はコアを小さく保ったままモジュールで利用する機能を取捨選択することができます。モジュールへの依存も最小のインターフェースで疎結合を保っているので、環境の変化への追随も比較的限定できるのではないかと考えています。
自分の足を撃ちにくいこと
この基準も Rails での経験によるものです。
Rails では Struts 時代の xml hell への反省から Convention over Configuration (CoC) という考え方が生まれて規約とリフレクションで振舞いが定義されることになりました。
CoC では規約で想定されているユースケースの範囲内であればスムーズに最小限のコードで実装できます。ですが、規約にあわない要件を実現するためにに非常にトリッキーな実装が必要になったり、リフレクションと動的コード生成が多用されるスタックトレースを見てもどこに問題があるのかさっぱりわからない、といった状況が発生します。内部でどのように動いているのかわかりづらい実装、規約という暗黙のルールへの依存は、自分の足を撃つトリガーになってしまうのです。
Jooby はリフレクションやアノーテーションは最小限に抑えられており、コードを追えば理解できる素直でシンプルな設計ポリシーにもとづいています。
Rails と同等レベルの生産性
これは明示的に設定した選定基準ではありませんでしたが、これまで長らく Rails を使ってきたため同等レベルの生産性(機能性)が保証されないとストレスを感じてしまう、というのも考慮した点でした。
型安全性で改修やフレームワークやライブラリのバージョンアップは確実に楽になります。ですが、その代わりに新規にコードを書くときに大量のボイラープレートが必要となってしまっては、これまでの環境から良くなったとは言えません。実装すべき機能に集中してサクサク書ける手軽さ、心地良さも重要な選定基準です。
エコシステムこそ小さいものの Jooby はモダン Web フレームワークが持つべき必要十分な機能を提供しており、IDE の補助も相まって、Rails と比べても同等以上に気持ちよくコードが書けるフレームワークだと感じています。
他の選定候補の評価
ここまで Jooby の素晴らしさを語ってきました。今度は逆に他の候補を採用しなかった理由、どの選定基準で採用を取りやめたのかを説明したいと思います。
Spring Boot
Java でのサーバーサイド開発ではデファクトスタンダードとなるフレームワークで、エコシステムの充実ぶりは他の追随を許しません。ですが、以下の観点から NG となりました。
型安全でない
アノーテーションを多用するフレームワークですが、アノーテーションはメタ情報でしかなく、コンパイル時に正しいかどうかを保証してくれるわけではありません。テンプレートとして共に利用されることの多い Themeleaf も動的評価型のエンジンです。
自分の足を撃ちやすい
Spring Boot は Auto Confuguration という仕組みで設定ファイルを減らす仕組みを持っています。この仕組みは classpath 上にある class をスキャンしてアノーテーションをもとに自動的にインスタンスを生成しコンテナに登録してくれます。一見便利に見えますが、問題があったときにその原因を特定するのが非常に困難な仕組みになります。
他の例では、実行可能な single file jar で配備することが可能ですが、その jar フォーマットは独自定義で、単純な jar ファイルではなく専用のブートクラスローダーによって処理されます。
sparkjava
Java での軽量フレームワークと言えば最初にあがる候補だと思います。エコシステムも大きく有名どころでは Asana が採用しているという実績を持ちます。
シンプルで学習コストの低いフレームワークではあるのですが、「Rails と同等レベルの生産性」という観点で見たときに、標準で提供される機能があまりに少ないがため選外となりました。
例を挙げれば、Jooby では標準で提供されているアクセスログ、env, cookie session、ファイル出力、非同期処理はサポートされていません。Jooby モジュールで提供される追加機能もあわせて考えれば言わずもがなです。
データベースアクセス層とテンプレート
Jooby が提供するのは Web 層だけなので、それ以外の選択についても簡単に説明したいと思います。
私達はデータベースアクセス層には JOOQ を、テンプレートエンジンには Rocker を採用しました。どちらも Jooby モジュールが提供されています。Jooby 同様に型安全性を重視して選択しました。
JOOQ
Rails ActiveRecord の scope のように条件を変数やメソッドとして切り出して組み合わせることができ、かつ型安全性が保証された SQL Builder ライブラリです。
Rocker
最近の Web アプリケーションではフロントエンドで UI を構成して、サーバーサイドは API だけを提供するスタイルを取ることが多くなりました。
ですが、SEO が重要なプロジェクトではやはりサーバーサイドレンダリングが確実です。googlebot は Chrome 41 相当でフロントエンドでのレンダリング結果もかなり正確にインデックスしてくれますが、Facebook Open Graph や Twitter Card をはじめとする SNS を考えるとサーバーサイドレンダリングはまだまだ必要です。
Rocker は Java で実装された型安全なテンプレートエンジンです。明記はされていませんが、Play Framework の Twirl テンプレートエンジン同様に、その構文は asp.net の Razor に影響を受けているように見受けられ、テンプレート言語としての仕様の成熟度は高いと判断しました。
最後に
ここまで様々な観点から私達が選んだ Jooby の魅力とその選定理由について説明してきました。Jooby はまだまだ若くエコシステムの小さな成長途上にあるフレームワークですが、Kotlin でのサーバーサイド開発の選択肢として、自信を持ってオススメできるプロダクトです。
皆さんが Kotlin でのサーバーサイド開発を検討される際に、この記事が一助となれば幸いです。
最後に、Jooby という素晴らしいフレームワークを開発された @edgarespina さんへの感謝で本記事を締めたいと思います。Happy Kotlin Coding with Jooby!