Open21

【自分向け】Kotlinことはじめ

TKTK

以前軽く触ったことはあったが、改めて環境構築。
基本的に自分用メモです。
使用マシンは、M1 Macbook Air。

まずはHomebrewを使ってインストール。

$ brew install java

その後パスを通す。

.zshrc

export PATH=/opt/homebrew/opt/openjdk/bin:$PATH

参考

TKTK

わりと最近作ったのにもう動かし方を覚えていない...。

少しおさらいから。

Kotlinについて

KotlinはJetBrains社が作った言語。
そのため、JetBrains社のIDEであるIntelliJ IDEAを入れると環境構築ができるというワケ。
特徴としては、Javaの拡張言語的な立ち位置で同居できること。
Javaからの移行先言語として採用されることも多い。
ざっくり言うと、「Javaが動く環境で動くJavaよりイケてる言語」という感じ。多分。

Javaについて

オブジェクト指向でおなじみの言語Java。

大きく分けて2種類ある様子。

  • Open JDK
  • Oracle JDK

とりあえず今後はOpen JDKを使っとけばOKって感じ?

あれ、これこっちでインストールしたほうがいいのか...?

$ brew install openjdk

ひとまず動かすところまでやってから考える。

参考

TKTK

Gradleについて

NodeJSにおけるpackage.json的なもの?
パッケージ管理とか、ビルドとか、諸々のタスク管理とかできる感じ?

Groovyという言語で build.gradle ファイルを作成するのが従来のやり方で、Kotlinを使った場合は、 代わりに build.gradle.kts ファイルを作成して利用することができる。
記述方法は若干の違いがあるが、基本的には同じようなことができるはず。

参考

Gradlewについて

Gradlewは、特定のGradleのバージョンを指定して、そのプロジェクトの開発者全員のGradleのバージョンを統一させることができるものである。
そのため、プロジェクトのgitに含めて利用する。
Gradleをインストールした状態で、下記のコマンドを実行することで、gradlew用のファイル一式を生成できる。

$ gradle wapper

wrapper タスクの記載が不要になった模様。
普通に作っちゃってたので、後で消そう...。

参考

TKTK

アーキテクチャ周りのSeedWorkをちょっとだけ整理して、1ユースケース追加。

https://github.com/k-takahashi23/ktor-api-example

ルーティングのプレースホルダーは、下記のように取得できるっぽい。

route("/users/{id}") {
    get {
        val id = call.parameters["id"]
        // ...
    }
}

型がnull許容になるのが微妙なので、あとでなにかいい方法がないか調べる。

参考

TKTK

POSTリクエストでBodyを取得する場合は、receive を使うのが良い様子。

post {
    val request = call.receive<AddUserUsecaseRequest>()
    // ...
}

ジェネリクスで指定するクラスをdata classにして、@Serializable アノテーションをつけておく必要がありそうなので注意。

@Serializable
data class AddUserUsecaseRequest(
    val name: String,
    val email: String,
): UsecaseRequest

参考

TKTK

UUIDの生成は非常に簡単だった。

val newUserId = UUID.randomUUID().toString()
TKTK

うーん、3.x系の情報が少なくていまいち動かない...。

https://insert-koin.io/docs/setup/v3

これも必要そう...?

build.gradle.kts

implementation("io.insert-koin:koin-core-ext:3.0.1")
implementation("io.insert-koin:koin-ktor:3.0.1")
implementation("io.insert-koin:koin-logger-slf4j:3.0.1")
TKTK

できた。
Koinより簡単だった印象。

dependenciesには、Ktor用のモジュールを追加。

build.gradle.kts

implementation("org.kodein.di:kodein-di-framework-ktor-server-jvm:7.3.1")

DI設定はこんな感じ。
instance() で内包するインスタンスもよしなにDIしてくれる模様。

fun DI.MainBuilder.bindServices() {
    bind<UsersRepository>() with singleton { UsersRepositoryImpl() }
    bind<FindAllUsersUsecase>() with singleton { FindAllUsersUsecaseImpl(instance()) }
    bind<AddUserUsecase>() with singleton { AddUserUsecaseImpl(instance()) }
    bind<FindUserByIdUsecase>() with singleton { FindUserByIdUsecaseImpl(instance()) }
}

サーバー側の設定はこんな感じ。

di {
    bindServices()
}

インスタンスを使う側はこう。

val findAllUsersUsecase by di().instance<FindAllUsersUsecase>()
val addUserUsecase by di().instance<AddUserUsecase>()
val findUserByIdUsecase by di().instance<FindUserByIdUsecase>()

Koinで苦戦したのはなんだったんだ...。
バージョン3.xを使おうとしたのが時期が悪かったかな。
暫定Kodein採用でいきます。

参考

TKTK

これだけでハンドリングしていないExceptionをキャッチしてくれるっぽい。

install(StatusPages) {
    exception<Throwable> {
        call.respond(HttpStatusCode.InternalServerError)
    }
}

全部500で処理するわけにもいかないので、クライアントエラーの機構は実装してみる。
APIリクエストのバリデーションあたりを組み込めるか調査する。