Kotlin + Spring Boot + Doma2 の環境を構築する

私は「現職で利用されているから」という理由で Java の学習を始めた。しかし、現職のメインの開発言語は Java から Kotlin へ移行している。学習開始頃の私は、メイン言語である Kotlin の学習始めたが、Java 周辺技術やオブジェクト指向プログラミングの知識などが乏しく、とりあえず Java を触ろうと考えていたのだった。それを最近思い出した。

前回、書籍「ユースケース駆動開発実践ガイド」に登場する ICONIX プロセスを用いて、すごく簡単に JavaREST API の設計をしてみた。Java で作る前提で設計していたが、当初の目的に立ち返って Kotlin で作ることにした。

kazuhe.hatenablog.com

余談

記事タイトルから逸れるが、Java と Spring Boot を利用してプロトタイプを作った際に読んだ以下の書籍について、雑なメモが残っていたので思い出すために整理する。

www.shoeisha.co.jp

この書籍は、Spring Framework をはじめ、Spring Web や Spring Security など、Spring で扱う主なプロジェクトの紹介から始まる。その後、DI の必要性など、最低限必要な前提知識の説明があり、ハンズオン形式で Spring Boot を用いた Web アプリケーションを作成していく。

Spring 周辺のエコシステムは、長い歴史からあまりに大きいため、どのプロジェクトがどの様に関連しているのか、何をどの様に利用すればよいのか初学者には把握が難しいと思うが、この書籍を読むことで Starter の存在や、関連や仕組みなどを何となく把握できる。

一方で、Bean という概念についての説明はされていないが、「Spring Boot における Bean 定義について理解することは非常に重要だ」と書かれていて、そこから先を読むために、Bean って何やねんを別途理解する必要があった。Spring Boot が裏でいい感じにしてくれていると思いながら付与していた @Service アノテーション@Controller などは、クラスを Bean として登録するための @Component というアノテーションを利用している。つまり、@Service アノテーションを付与すると、その中で @Component を利用して Bean に登録され、Spring Boot アプリケーションの中で再利用できる様に ApplicationContext という箱に詰められ、私たち開発者は意識することなく利用できていたらしい(大まかにしか理解していない)。Spring Boot は便利すぎるため、知ろうとしないと中で何が起きているのか分かりにくいので、必要に応じて調べようと思う。

Kotlin 入門 + Doma2 の設定

Javaオブジェクト指向も Spring もよく分からない頃、とりあえず現職のバックエンド技術に合わせて読んだ書籍「Kotlin サーバーサイドプログラミング実践開発」を再度読んでみた。

gihyo.jp

当時は読んで書いてその通り動かすことはできたがわりと難しく感じた。Java や Spring を少し学習して改めて読むと以前と比較して難しさは感じていない。

とりあえず Kotlin の基本的な機能などの説明を読んで、試しに書いて動かして、本格的にアプリケーションを作成する 6 章の冒頭まで読んだ。このハンズオンでは O/R マッパーに MyBatis を利用しているのだが、私は Doma2 を利用したかったので、紹介されている構成と変えながら進めている。

Doma2 の公式ドキュメントには、サンプルプロジェクトのリンクがあるだけで、詳しいインストール方法は書かれていない。少し調べながら環境を作ったのでメモ的に簡単にまとめる。Node.js ベースの環境構築は慣れているが、Java や Kotlin の環境構築は分からないことが多い。

doma.readthedocs.io

私は今回 Kotlin, Spring Boot, Gradle の構成なので、README.md を見ながら build.gradle.kts に以下の依存を追加した。

dependencies {
    implementation("org.seasar.doma:doma-kotlin:2.53.3")
    kapt("org.seasar.doma:doma-processor:2.53.3")
}

ここで唐突に kapt が登場するが、plugins に kapt の宣言をする必要があるらしい。

plugins {
    kotlin("kapt") version "1.8.21"
}

よく見ると README.md の下の方の Related projectsdoma-spring-boot のリンクがあり、これの README.md を見て以下を追加した。

dependencies {
    ...
    implementation("org.seasar.doma.boot:doma-spring-boot-starter:1.7.0")
}

ここまでの設定で利用できそうに思うが、正しく動作しなかった。私は DAO Style かつ SQL テンプレート?を利用する前提で、ChatGPT 先生や、Google 先生に質問すると、以下の様な記述が必要だと教えてくれた。

val compileKotlin: KotlinCompile by tasks
kapt {
    arguments {
        arg("doma.resources.dir", compileKotlin.destinationDirectory)
    }
}
tasks.register("copyDomaResources", Sync::class) {
    from(sourceSets.main.get().resources.srcDirs)
    into(compileKotlin.destinationDirectory)
    include("doma.compile.config")
    include("META-INF/**/*.sql")
    include("META-INF/**/*.script")
}
tasks.withType<KotlinCompile> {
    dependsOn(tasks.getByName("copyDomaResources"))
    ...
}

公式ドキュメントに同様の説明が見当たらなかったので、疑いながら調べていると doma-compile-plugin という素晴らしいプラグインを発見した。これは、上記の様なコンパイルの設定をしてくれるプラグインらしい。よく見るとこれも Related projects にリンクがある。ここまで調べるのに時間がかかった。これは気付けないよと思いながらプラグインとして追加した。

plugins {
    ...
    id("org.domaframework.doma.compile") version "2.0.0"
}

最終的な build.gradle.kts は以下の様になった。

import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

plugins {
    id("org.springframework.boot") version "2.7.11"
    id("io.spring.dependency-management") version "1.0.15.RELEASE"
    id("com.diffplug.spotless") version "6.18.0"
    kotlin("jvm") version "1.6.21"
    kotlin("plugin.spring") version "1.6.21"

    // Doma のために追加した
    kotlin("kapt") version "1.8.21"
    id("org.domaframework.doma.compile") version "2.0.0"
}

group = "x"
version = "0.0.1-SNAPSHOT"
java.sourceCompatibility = JavaVersion.VERSION_11

repositories {
    mavenCentral()
}

dependencies {
    implementation("org.springframework.boot:spring-boot-starter-web")
    implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
    implementation("org.jetbrains.kotlin:kotlin-reflect")

    // Doma のために追加した
    implementation("org.seasar.doma:doma-kotlin:2.53.3")
    kapt("org.seasar.doma:doma-processor:2.53.3")
    implementation("org.seasar.doma.boot:doma-spring-boot-starter:1.7.0")

    implementation("mysql:mysql-connector-java:8.0.32")
    testImplementation("org.springframework.boot:spring-boot-starter-test")
}

tasks.withType<KotlinCompile> {
    kotlinOptions {
        freeCompilerArgs = listOf("-Xjsr305=strict")
        jvmTarget = "11"
    }
}

tasks.withType<Test> {
    useJUnitPlatform()
}

spotless {
    kotlin {
        ktlint()
    }
}

諸事情により Java11 を利用しているが、17 やそれ以降も使えるらしい。

Spring Boot を BootRun すると、Docker を利用して立ち上げた MySQL から値を取得することができた。Doma2 を利用するために、複数のプラグインや依存が必要なのが難しく感じた。

これから

書籍「Kotlin サーバーサイドプログラミング実践開発」の 6 章以降を参考に、前回作った設計の実装を進めていく。

そういえば、JavaVSCode で書けたけが、さすがに Kotlin を書くには限界を感じたので IntelliJ IDEA に移行した。