Spring Bootでマルチプロジェクト構成

# はじめに
SpringBootでマルチプロジェクト構成を試してみました。

# 環境
Java:17
SpringBoot：3.1.1
Gradle：8.1.1

# 1.全体像
下記、図のようにルートプロジェクト配下にサブモジュールプロジェクトを配置します。
ルートプロジェクトでbuild.gradleを配置し、両サブモジュールプロジェクトで共有させます。

```
multi-project（ルートプロジェクト）
├── multi-project-A（サブモジュールプロジェクト）
│   └── src/main
│       ├── java
│       │    └── パッケージ
│       │          └── SampleAController.java
│       └── resources
│            └── application.yml
│       
├── multi-project-B（サブモジュールプロジェクト）
│   └── src/main
│       ├── java
│       │    └── パッケージ
│       │          └── SampleBController.java
│       └── resources
│            └── application.yml
│
├── build.gradle
├── gradlew
├── gradlew.bat
└── settings.gradle
```

# 2.ルートプロジェクトを作成
## Spring Initializrでプロジェクトを作成
ルートプロジェクトでは、Gradleを実行する必要があります。
今回は、[Spring Initializr](https://start.spring.io/)でGradleプロジェクトを作成して用意します。

作成したプロジェクトで必要なのは以下の4つ。
・build.gradle
・gradlew
・gradlew.bat
・settings.gradle

他は削除します。

## settings.gradleを設定
Spring Initializrでプロジェクトを作成すると、デフォルトでルートプロジェクトが設定されています。
加えて、include()でモジュールプロジェクトを指定します。

```settings.gradle
rootProject.name = "multi-project"
include("multi-project-A")
include("multi-project-B")
```

## build.gradleを修正
ルートプロジェクトのbuild.gradleを修正します。
適用するプラグインや依存関係、Junitの設定をsubprojects内に記述するようにします。

リポジトリはallprojectsに記述していますが、subprojectsでも動作します。

```build.gradle
plugins {
	id 'java'
	id 'org.springframework.boot' version '3.1.1'
	id 'io.spring.dependency-management' version '1.1.0'
}

allprojects {
	repositories {
		mavenCentral()
	}
}

subprojects {

	apply plugin: 'java'
	apply plugin: 'org.springframework.boot'
	apply plugin: 'io.spring.dependency-management'
	group = 'com.sample.multi-project'
	version = '0.0.1-SNAPSHOT'

	java {
		sourceCompatibility = '17'
	}

	configurations {
		compileOnly {
			extendsFrom annotationProcessor
		}
	}

	dependencies {
		implementation 'org.springframework.boot:spring-boot-starter-web'
		compileOnly 'org.projectlombok:lombok'
		annotationProcessor 'org.projectlombok:lombok'
		testImplementation 'org.springframework.boot:spring-boot-starter-test'
	}

	tasks.named('test') {
		useJUnitPlatform()
	}
}
```

# 3.モジュールプロジェクトを作成
## Spring Initializrでプロジェクトを作成
ルートプロジェクト同様にGradleプロジェクトを作成します。

## build.gradleを削除、または空
モジュールプロジェクト側には不要です。

# 4. モジュールプロジェクトにAPIを実装
動作確認のため、各モジュールプロジェクトに「Hello World」を返すだけのコントローラークラスを実装します。

```SampleAController.java
package com.sample.multiproject.A.multiprojectA;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class SampleAController {

    @GetMapping("sampleA")
    @ResponseBody
    public String index() {
        return "hello worldA";
    }
}
```

```SampleBController.java
package com.sample.multiproject.B.multiprojectB;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class SampleBController {

    @GetMapping("sampleB")
    @ResponseBody
    public String index() {
        return "hello worldB";
    }
}
```

# 5. モジュールプロジェクトのポート設定
モジュールプロジェクトがリクエストを待ち受けるポートをそれぞれ設定します。
application.ymlでポートを設定します。

まず、multi-project-Aは80番で待ち受けるように設定

```application.yml
server:
  port: ${PORT:80}
```

続いて、multi-project-Bは70番で待ち受けるように設定

```application.yml
server:
  port: ${PORT:70}
```

# 6. 動作確認
## アプリ起動
GradleタスクのbootRunでSpringBootアプリを実行します。

```shell
% ./gradlew :multi-project-A:bootRun
% ./gradlew :multi-project-B:bootRun
```

## リクエスト
アプリが起動したらリクエストし、それぞれの動作を確認します。

```shell
% curl http://localhost:80/sampleA
hello worldA
% curl http://localhost:70/sampleB
hello worldB
```

# 参考文献
https://spring.pleiades.io/guides/gs/multi-module/

https://docs.gradle.org/current/userguide/declaring_dependencies_between_subprojects.html

https://docs.gradle.org/current/dsl/org.gradle.api.Project.html

https://zenn.dev/boronngo/articles/gradle-multi-project