現在、サーバサイド Kotlin でシステム開発を行なっています。Kotlin 初心者の状態から開発を始めてから7ヶ月ほど経ち Kotlin でコードを書くことにもだいぶ慣れてきたので、どのように学習を行って慣れたのかと、採用したライブラリの良かった点、開発中にハマった点の以上3点をまとめました。
自己紹介
まず、どのような人物が Kotlin 初心者の状態で開発を始めたのか基本的な情報があったほうがいいと思ったので、簡単ですが経歴を以下に記載しました。
- 従業員規模500名程度の金融系 SIer(SES で現場に行くのが主な仕事) に2006~2015年まで在籍
- その後転職し、2016年からフリーランスに転身
- 要件定義、設計、開発、テスト、運用など一通りの経験がある
- 主な開発言語は Java
- Java は 4から8を使用 → ラムダ式や stream API は問題なく使用できる感じ
- Spring Boot は Java で結構使っていたのでそれなりにわかる
- Eclipse を長年使用していた → 今は IntelliJ IDEA
どのように Kotlin を学習したか
今のプロジェクトで Kotlin を本格的に始めるにあたって、まずは Kotlin Webアプリケーションプログラミング を購入して Kotlin の基本的な文法を一通り学習しました。以前 Swift でアプリを作っていたので、文法が Swift に似てるなーという感じがあり、文法的な部分は特に苦労しなかったです。Java の stream API のような処理も Kotlin だと簡単に書くことができるので、map
や let
などの使い方も割とすんなり覚えられたと思います。Kotlin の基本的な文法はシンプルなので、何らかのプログラミング経験があれば容易に習得できるのではないかなと。だいたい1〜2ヶ月くらいであまりググらずに Kotlin を書けるようになったと思います。
あとは、今いるプロジェクトのメンバーが Kotlin を書いていたので、コードレビュー時にイケてる関数の使い方や Kotlin っぽいコードの書き方を教えてくれたことと、モブプロでどんな風にコードを書けばいいかを学習できたのが大きかったかなと思います。現在は Kotlin にもだいぶ慣れてきたので、Kotlin をより深く理解するために Kotlin in Action を読んでいます。学ぶことが多く、非常にいい本だと思います。
Kotlin の良い点
- null safety
- data class
- 拡張関数、高階関数
特に長年 Java をやっていた身からすると NullPointerException の発生をコンパイラレベルで抑えてくれるのは非常にありがたいです。Java のOptional
で頑張るのもそれはそれでつらみがある・・と思っていたので、Kotlin の null 許容型とその扱い方を知った時は感動しました。
続いて、現在開発中のシステムで採用している技術スタック周りの話です。
基本構成
-
GKE
- Java 11
- Kotlin 1.3.10
-
GAE/Java
- Java 8
- Kotlin 1.3.10
GAE/Java と GKE にデプロイする別々のサービスを開発しており、ライブラリは同じものを採用しています。GAE に関しては GAE/Java Standard の制約により、Java 8 を使用しています。
フレームワークとライブラリ
- Spring Boot 2.1.0
- Gradle 4.10.2 (書くことがあまりないので割愛)
- jOOQ 3.11.7
- SpringFox 2.9.2
- assertJ 3.10.0 (書くことがあまりないので割愛)
- assertJ-db 1.2.0
- liquibase 3.6.2 (書くことがあまりないので割愛)
- dbSetUp 2.1.0
- factlin 0.2.0
- mockito-kotlin 1.5.0
最近はサーバサイド Kotlin を採用する事例がそれほど珍しくなくなってきた感があるので、サーバサイド Kotlin で Spring Boot を採用しているプロジェクトが多いかと思います。国内で jOOQ を使っているというのはあまり聞いたことがないのですが、海外では結構採用されているイメージです。
Spring Boot 良い点
Java での使用実績が豊富であること、ネット上に情報が多い、慣れているのでハマりポイントが少ないの3点です。Kotlin でも問題なく動作するので、Java で Spring Boot を用いた開発経験があれば特に違和感なく使用できるのではないかと思います。また、Spring 自体が Kotlin を手厚くサポートしているので、フレームワークを採用するにあたっての有力な選択肢の1つではないかなと思います。
Spring Boot ハマりポイント
Spring 自体が巨大なフレームワークなため、Spring 未経験のメンバーがジョインした際にそれなりの学習コストがかかる点です。大量に用意されている annotation を理解することや AutoConfiguration 周りで想定していない挙動になることがあるので、Spring Boot がそれなりにわかる人が最低一人は欲しい感じがあります。概要を理解するのであれば、内容は Spring Boot 1系になりますが はじめての Spring Bootを写経し、その上で Spring 徹底入門 と Spring 入門を読むと良いかと思います。あとは terasoluna のガイドラインにも情報がまとまっているので、困ったら見てみると解決策や方針が書いてあることがあります。
jOOQ 良い点
学習コスト低めです。jOOQ のサンプルに記載されている通り、タイプセーフな SQL を Kotlin でお手軽に書ける点が最大のメリットかなと思います。最悪、ネイティブクエリを実行することもできるので、数百行を超えるような SQL や大量のテーブルを join するような SQL を実装しなければならないようなケースでも対応できるようになっています。jOOQ で @Repository
のテストを書いた場合は @JooqTest
を付与してテスト実行するとサーバが起動しないので、Spring の DI のみ実行された状態でテスト可能です。
jOOQ ハマりポイント
日本語の情報が少ないので、基本的に英語の情報と公式のドキュメントを見ながら設定することになります。jOOQ 独自の型の ULong
が Kotlin の ULong
と互換性がなく、MySQL の bigint
を使用すると jOOQ 独自の ULong
になるので型変換が面倒になります。コード自動生成がたまに動かなくなるので、jOOQ が自動生成したファイルに適当な改行を加えたりするとコード自動生成がうまく動いたりと、謎な挙動があります。大量のファイルを自動生成して git などで管理すると差分が大量に発生して確認が難しくなるため、自動生成ファイルの扱いをどうするかは最初に決めた方がいいでしょう。if を使用して条件によって sql の組み立てを行うと実行される sql が分かりにくくなるので、コードレビューでバグを検知するのが困難になります。実行する sql を確実に抑えておきたいという要望があるのであれば Doma2 などを採用するのがいいのかなと思います。
SpringFox 良い点
@RestController
などのコードから Swagger を生成するため、実装と Swagger の定義が乖離しません。Swagger の定義ファイルを生成し nodejs-server として起動できるので、e2e テストをするために mock サーバを建てて CI に組み込むといったことが可能です。
SpringFox ハマりポイント
@RestController
のクラスやリクエスト、レスポンスのクラスに対して Swagger の説明を記載するので、コードの見通しが悪くなります。Swagger の定義ファイルから nodejs-server を起動してリクエストを送る場合、example
が設定されていないと API の呼び出しに失敗する場合があります。
@ApiModel(value = "サンプルリクエスト")
data class SampleRequest(
@ApiModelProperty(value = "サンプルのID", required = true, example = "1")
val id: Int,
@ApiModelProperty(value = "サンプルの名前", required = true, example = "hogehoge")
val name: String
)
assertJ-db 良い点
データベースに登録したデータを assertでき、テスト用に SQL を発行した結果から assert したり、テーブル全体の変更を assert することができます。@Repository
層や mock を使用しない @Service
層の実行結果を assert しやすいです。
assertJ-db ハマりポイント
Changes
は指定したテーブル全体の変更を検知するので、テーブルのレコード数が多いと assert 実行に数十秒かかる場合があります。テストケースが少ない場合はたいしたボトルネックになりませんが、テストケースが数百を超えてくると CI のパイプライン実行が遅くなってくるので、基本的に Request
を使用して取得するレコードを絞って assert した方がテスト実行が早くなります。あとは、Changes
を使用する場合、1トランザクション内で同一テーブルに対して update と insert を行うとテーブルの変更をうまく検知できなくなるケースがありました。(isCreation
または isModification
で assertion error になってしまうのでテストが通らない)
dbSetUp 良い点
ハマりポイントは特になかったので割愛します。Kotlin DSL が用意されているので使い勝手が良くなりました。後述する factlin と併用するのがおすすめです。
factlin 良い点
テストデータの登録に dbSetUp を使用すると insert を手で書くのが非常に辛いところではありますが、factlin を使用するとテーブル定義から insert するためのdata class
を自動生成できるので、テストデータ作成が非常に楽になります。テストコード作成は結構力を入れているので重宝しています。
factlin ハマりポイント
MySQL で factlin を実行すると対象のスキーマが information_schema
になることがあるので、build.gradle
の spring_boot_version
を変更すると対象のスキーマに接続し build が成功します。時間があるときに原因を調べてみます。
mockito-kotlin 良い点
Mockito を Kotlin DSL でラップしたものなので、中身は Mockito です。Kotlin DSL で mock を書けるのでより簡潔に mock オブジェクトを作成できます。
mockito-kotlin ハマりポイント
final クラスの mock を作るために src/test/resources に mockito-extentions ディレクトリを作成し、org.mockito.plugins.MockMaker
というファイルを作成する必要があるのと、最近は mockK があるし mockK でいいんじゃないかという気持ちです。
まとめ
サーバサイド Kotlin を採用した事例が増えてきたこともあり、新規プロジェクトでサーバサイド Kotlin を始める方は今後増えていくのではないかと思います。自分のように長年 Java で開発を行ってきたエンジニアには学習コスト的にもちょうどいい言語ではないかなという感じです。現状はプロジェクトで採用したライブラリの大半は Java 製のもので賄っている状態ですが、今後は Ktor や Exposed を採用したシステムも出て来るかと思いますので、2019年は全て Kotlin 製のライブラリで構築されたシステムが見れるかも?と思いつつ、2019年も Kotlin をやっていけたらいいなと思います💪