データ層との連携
ビズリーチのようなWebサービスのデータ層としてRDBMSは不可欠です。しかしJavaとSQLは言語として異なります。SELECT文の実行結果はJavaBeansのListとして得られるほうが便利です。そこを橋渡しするのがO/Rマッパーと呼ばれるフレームワークです。
ビズリーチ社内では多くのプロジェクトで伝統的にDBFluteを採用しています。DBFluteは単なるO/Rマッピング機能だけではなく、テーブル定義書(html)の自動生成や、XLS/TSV形式で用意したテストデータの自動投入機能など、開発現場での作業を楽にする機能がてんこ盛りなので重宝しています。
最近ではjOOQ(ジューク)を導入しているプロジェクトも増えてきました。jOOQは非常に直感的にSQLを書くことができるうえ、DAOクラスやimmutableなPOJOクラスの自動生成機能もあるため、次世代のO/Rマッパーとして注目しています。また、あえてjOOQとSpring Data JPAの二つを組み合わせて使用しているチームもあります。SpringのTransactionAwareDataSourceProxyやPlatformTransactionManagerを使ってトランザクションさえ一元管理しておけば、複数のO/Rマッパーを同時に使い、そのいいとこ取りをすることができます。作り続ける過程でO/Rマッパーを追加したり乗り換えたりすることも可能でしょう。
なお、DBFluteやjOOQは、DBのテーブル定義をもとにそれにアクセスするためのJavaクラスを自動生成するツールでもあります。そこで、「自動生成したコードはVCSでのバージョン管理の対象とするべきか否か」が問題になります。DBFluteは自動生成コードもVCSで管理することが前提ですが、jOOQはその限りではありません。コード自動生成型のO/Rマッパーを選択するときは必ず試作をしながら、自動生成されたコードをどう管理するかをあらかじめ考えておくべきでしょう。
ログ出力戦略
Webアプリケーションのログは、地味ですが大切な構成要素です。アプリケーションの動作記録として監視し、不具合を早期に検知するためだけでなく、ログそのものがWebサイトのマーケティングのための貴重な分析材料となります。冒頭に紹介した12-Factor App戦略では、次のようなことが提唱されています。
Twelve-Factor Appはアプリケーションの出力ストリームの送り先やストレージについて一切関知しない。 アプリケーションはログファイルに書き込んだり管理しようとするべきではない。代わりに、それぞれの実行中のプロセスはイベントストリームをstdout(標準出力)にバッファリングせずに書きだす。
つまり、Webアプリケーションの仕事はすべてのログを標準出力することだけ。種類ごとにファイルに書き出したり、それを日別にローテーションしたりするのは、Webアプリケーションの仕事ではないのです。log4j.propertiesにログの種類ごとにロガー名を指定し、DailyRollingFileAppender……と書いていた努力はなんだったのでしょうか。
しかし、そうして肥大化したlog4j.propertiesと、ログディレクトリに無秩序に散らばった数々のログファイル(しかもログフォーマットはそれぞれ微妙に異なる)を目の前にすると、12-Factor App戦略にある「ログを一本化せよ」といった戦略は正しいと言わざるを得ません。Dockerのような、揮発性の高いコンテナ技術の活用まで考えると標準出力のみに集約すべきであり、そうするしかないでしょう。
ビズリーチシステムのように継続的にメンテナンスが続くアプリケーションにおいて、唐突にログをstdoutに集約するのは現実的とは言えません。しかし、既存のログ設定を残して従来の運用方式を担保しつつ、ある1つの統一されたログフォーマットと出力先ファイルを新設することは、実際には非常に簡単でした。統一されたフォーマットとは、JSONです。
古いlog4j1.x系であっても、log4j-jsonevent-layoutを追加すればJSON形式での出力は簡単です。logbackにもlogback-json-classicがありますし、それも不満ならレイアウト指定クラスを自作することも可能です。JSONフォーマットで一本化されたログには、動作監視用のログもマーケティング分析目的のログも、すべてを1つのファイルに集めます。例外のスタックトレースはタブ文字や改行が含まれますが、それらの文字はすべてエスケープすることでやや長めのJSONオブジェクトを示す1行のログとなります。
あとはもう、アプリケーションのログ出力に追加要件が発生しても、log4j.propertiesやlogback.xmlに設定を追加する必要はありません。一つに集約されたJSON形式のログをどう料理するかはfluentd(td-agent)などのログルーティングソフトウェアの設定次第です。監視機構や分析用データベースに流し込むことも、いったん別のログ集約専用サーバに集めてからAmazon S3のようなストレージに保存することも、すべてWebアプリケーションの外側で管理することができます。
なお、Javaの世界ではログ出力ライブラリが多数あります。伝統的なデファクトがlog4jです。しかしSpring Frameworkはcommons-loggingに依存しており、さらに最近のJavaライブラリはslf4j-apiを使用していることが多いです。slf4jはさまざまなロギングライブラリのブリッジを提供しています。jcl-over-slf4jはcommons-loggingのブリッジ、jul-to-slf4jはjava.util.loggingのブリッジです。こうしたブリッジライブラリに差し替えれば、既存のコードのlog4jによる出力はそのままに、新しく書いたコードについてはslf4j-api経由でlog4jに集めることで、あらゆるログ出力を集約することができるようになります。もちろんlog4jがベストということではなく、logbackなどの他のロギングライブラリに集約することも可能でしょう。
後編では、「バッチ処理」「Javaとクラウドサービス」「セキュリティ」「テスト」「バージョンアップ」についてお伝えします。