【Spring Boot入門】Spring Bootをマルチモジュール構成に分割してみた
🧩 はじめに
Spring Bootで作っていたSkillLogアプリが少しずつ大きくなり、
「そろそろ構成ちゃんと分けた方がよくないか?」と感じたのが、今回のモジュール分割のきっかけです。
この記事では、
- Spring Bootをモジュールごとに分割した理由
- 実際に分けた手順(詰まったエラー含む)
- やってよかったこと、改善点
を備忘録としてまとめておきます。
SkillLogアプリは、スキル学習ログを記録・分類する簡単なWebアプリです。
🧩 前提
当初は、以下のような単一構成でした:
小規模ならこれでも十分なのですが、次のような問題が出てきました:
- Web画面(Thymeleaf)用とREST API用の処理が、同じControllerに混ざってしまい、役割が曖昧になる
- テストやCI/CDを導入したくても、モジュールが1つにまとまりすぎていて、構成の整理がしにくい
- エンティティ(データ構造)やリポジトリを複数のアプリ層で使い回したいのに、依存関係がごちゃつく
🧩分割後の構成
最終的に以下の4モジュール構成に分離しました:
💻 実装内容
🔸 skill-log-parent作成
まずは、全体の親プロジェクト(skill-log-parent)を作成しました。
このモジュールの目的は、他のモジュール(web, api, domain)をまとめて管理することです。
この skill-log-parent モジュールには src/main/java などのコードディレクトリは不要です。
Mavenの設定だけを記述する “設定専用プロジェクト” として機能します。
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.5.0</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>skill-log-parent</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>pom</packaging>
<name>skill-log-parent</name>
<description>Demo project for Spring Boot</description>
<url/>
<modules>
<module>skill-log-app-web</module>
<module>skill-log-app-api</module>
<module>skill-log-domain</module>
</modules>
<properties>
<java.version>17</java.version>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
-
packaging = pom
これは「このモジュールは実行可能なアプリではなく、設定や構成のためだけのプロジェクトですよ」という意味になります。 -
各モジュールを
<modules>
で定義
parent のpom.xml
に以下のように書くことで、Mavenがすべての子モジュールを認識してくれます
<modules>
<module>skill-log-app-web</module>
<module>skill-log-app-api</module>
<module>skill-log-domain</module>
</modules>
- すべての子モジュールに共通して使いたい値を定義
<properties>
タグでは、すべての子モジュールに共通して使いたい値を定義できます。
今回は Java 17 を使用しているので、以下のように設定しています:
<properties>
<java.version>17</java.version>
</properties>
- プラグインの定義
親プロジェクトでは、Spring Boot のビルドや実行に使うプラグインも定義しており、子モジュール側ではこのプラグインを明示的に書かなくてもOKになります。
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
🔸 各モジュールの pom.xml 作成
各プロジェクトで親プロジェクトを明示的に参照
すべてのモジュール(web, api, domain)では、skill-log-parent
を親として継承するように、それぞれの pom.xml
に <parent>
を定義します。
<parent>
<groupId>com.example</groupId>
<artifactId>skill-log-parent</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath>../skill-log-parent/pom.xml</relativePath>
</parent>
- なぜrelativePathが必要?
Maven はデフォルトで親プロジェクトをローカルリポジトリや中央リポジトリから探そうとするため、ローカルで管理している親プロジェクトを正しく参照するには、relativePath でファイルの場所を明示する必要があります。
特にマルチモジュール構成では、相対パスで parent を見つけられないとビルドエラーになるため注意が必要です。
各プロジェクトでskill-log-domain を依存に追加
skill-log-domain
モジュールには、エンティティ(Entity)やリポジトリ(Repository)、サービスロジックなど、アプリ全体で共通して使いたい「ドメイン層の部品」をまとめています。
そのため、Web画面(Thymeleaf)を扱う skill-log-app-web
や、REST API を提供する skill-log-app-api
から、これらのクラスを使いたいときには、それぞれの pom.xml
に skill-log-domain
を依存として追加する必要があります。
<dependency>
<groupId>com.example</groupId>
<artifactId>skill-log-domain</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
各プロジェクト固有の依存ライブラリを追加
例えば、skill-log-app-web
の場合は、下記のような依存ライブラリを追加しています。
ライブラリ | 用途 |
---|---|
spring-boot-starter-web |
MVCベースのWebアプリケーションの基本構成(Controller, Dispatcherなど) |
spring-boot-starter-thymeleaf |
HTMLテンプレートエンジン(画面の動的描画に使用) |
spring-boot-starter-data-jpa |
DBアクセス(Repository, Entity管理) |
jakarta.validation-api |
入力バリデーション(フォームチェックなど) |
spring-boot-devtools |
開発用のホットリロード支援(再起動の効率化) |
lombok |
エンティティクラスなどの冗長なコードを簡略化(@Getterなど) |
h2 |
開発時用のインメモリデータベース |
spring-boot-starter-test |
単体テスト・統合テスト用ライブラリ |
skill-log-domain |
ドメイン層の再利用(Entity, Repository, Service など) |
このように、Webモジュールでは「画面表示・DBアクセス・バリデーション・開発支援」に必要な依存を中心に構成しています。
一部抜粋
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
💡 よくある質問:モジュールが自動で読み込まれない
IDEによっては skill-log-parent
をimportしても、下位モジュール(web, api, domain)が表示されない場合があります。
その際は、以下を試してみてください
- プロジェクト右クリック → Maven → Update Project
-
.project
/.classpath
の破損確認 -
.m2/repository
の再構築
🧠 学び・気づき
- 不明瞭だった責務が整理された
- GitHubに公開しても「構成がしっかりしてる感」が出せる
- セキュリティ、テスト、CI/CDなどを今後安心して追加できる
今後やりたいこと
- Spring Security導入(ログイン機能)
- REST APIを拡充 → フロントと切り離したSPA化の検討
- GitHub ActionsやDocker対応
自作アプリを「作品」から「実践的なポートフォリオ」にするには、こうした構成面の整理が非常に大事だと感じました。
📝 さいごに
分離作業は正直面倒な部分も多かったですが、Spring Bootアプリを育てる上で避けては通れない設計課題でもあります。
この記事が、モジュール分離を考えている人の参考になればうれしいです!
Discussion