KotlinでSpringBootのマルチプロジェクト

Kotlin+SpringBootでマルチプロジェクトを行う方法です。

私の利用方法としては、プレゼンテーションレイヤーとそれ以降のレイヤーを分離しマルチプロジェクトにすることで、例えばWebアプリケーションとイベント操作するアプリケーション(Kafkaなど)で同じドメイン知識を使い回すなどに利用しています。

## マルチプロジェクトの作成

今回のディレクトリ構成です。

```:構成
.(Root)
├── share  (共通ライブラリとして利用するプロジェクト)
│   ├── src/main/kotlin
│   │   └── com.example.demo.service
│   │       └── HelloService.kt
│   └── build.gradle
├── web  (アプリケーションプロジェクト)
│   ├── src/main
│   │   ├── kotlin
│   │   │   └── com.example.demo
│   │   │       ├── DemoApplication.kt
│   │   │       └── controller
│   │   │          └── HelloController.kt
│   │   └── resources
│   │       └── application.yml
│   └── build.gradle
├── build.gradle
└── setting.gradle
```

### setting.gralde修正
マルチプロジェクトにする場合はsetting.gradleに設定を行います。

includeにサブプロジェクトを指定します。

```grade:setting.gradle
rootProject.name = 'demo'
include 'web', 'share'
```

### build.gradleの修正

まずはプロジェクトrootのbuild.gradleを修正します。

```gradle:build.gradle
plugins {
    id "org.springframework.boot" version "2.2.6.RELEASE"
    id "io.spring.dependency-management" version "1.0.9.RELEASE"
    id "org.jetbrains.kotlin.jvm" version "1.3.71"
    id "org.jetbrains.kotlin.plugin.spring" version "1.3.71"
}

ext {
    springBootVersion = "2.2.6.RELEASE"
}

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

allprojects {
    repositories {
        mavenCentral()
    }
}

subprojects {
    apply plugin: 'kotlin'
    apply plugin: 'org.springframework.boot'
    apply plugin: "io.spring.dependency-management"

    dependencyManagement {
        imports {
            mavenBom "org.springframework.boot:spring-boot-dependencies:$springBootVersion"
        }
    }

    dependencies {
        implementation "org.springframework.boot:spring-boot-starter-web"
        implementation "com.fasterxml.jackson.module:jackson-module-kotlin"
        implementation "org.jetbrains.kotlin:kotlin-reflect"
        implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
        testImplementation("org.springframework.boot:spring-boot-starter-test") {
            exclude group: "org.junit.vintage", module: "junit-vintage-engine"
        }
    }

    test {
        useJUnitPlatform()
    }

    compileKotlin {
        kotlinOptions {
            freeCompilerArgs = ['-Xjsr305=strict']
            jvmTarget = '11'
        }
    }

    compileTestKotlin {
        kotlinOptions {
            freeCompilerArgs = ['-Xjsr305=strict']
            jvmTarget = '11'
        }
    }
}
```

### webプロジェクト

webプロジェクトはコントローラなどのプレゼンテーションレイヤーとしての役割を想定しています。

#### build.gradle

webプロジェクトのbuild.gradleです。
KotlinでのmainClassは`class名+Kt`になりますのでご注意ください。

```web/build.gradle
bootJar {
    mainClassName = "com.example.demo.DemoApplicationKt"
}

dependencies {
    implementation project(":share")
}
```

#### コード

```kotlin:DemoApplication.kt
package com.example.demo

import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication

@SpringBootApplication
open class DemoApplication

fun main(args: Array<String>) {
    runApplication<DemoApplication>(*args)
}
```

```kotlin:HelloController.kt
package com.example.demo.controller

import com.example.demo.service.HelloService
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RestController

@RestController("/")
class HelloController(
    private val helloService: HelloService
) {
    @GetMapping()
    fun getHello(): String {
        return helloService.say()
    }
}
```

その他`application.properties`(yams)はこちらのプロジェクトに配置してください。
今回は特に何も記載していません。

### shareプロジェクト

shareプロジェクトはサービスなどのドメイン知識など配置するレイヤーとしての役割を想定しています。

#### build.gradle

shareプロジェクトでのbuild.gradleでは以下のような設定にしています。
shareプロジェクトはライブラリ的な扱いになり、それ単体で実行可能アプリケーションとはしないため bootJar無効にして、jarを有効にしています。

その他外部のライブラリなどを使いたい場合は、dependenciesに追加します。

```gradle:share/build.gradle
bootJar {
    enabled = false
}
jar {
    enabled = true
}

dependencies {
}
```

#### コード

```kotlin:HelloService.kt
package com.example.demo.service

import org.springframework.stereotype.Service

@Service
class HelloService {
    fun say(): String {
        return "Hello!"
    }
}
```

### buildと実行

ではbuildして実行します。

```console:build
$ gradle :web:build
Starting a Gradle Daemon (subsequent builds will be faster)
<-------------> 0% CONFIGURING [14s]

BUILD SUCCESSFUL in 1m 28s
7 actionable tasks: 7 executed

$ ls -l web/build/libs/
total 43296
-rw-r--r--  1 ysaito  staff  22167040  5  2 22:31 web.jar
```

```console:実行
$ java -jar web/build/libs/web.jar 

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.2.6.RELEASE)

--一部省略--
2020-05-02 22:33:08.984  INFO 1825 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Completed initialization in 8 ms

$ curl localhost:8080/
Hello!
```


## 参考
[Spring Guides -> Creating a Multi Module Project](https://spring.io/guides/gs/multi-module/)