😎

【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.xmlskill-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