はじめに

この記事は、Java で自作クラスが 10 個を超えてきた途端に「どこに置いたらよかったっけ?」と混乱し始めた中級者の方を対象にしています。
個人開発や小規模開発では「とりあえず src に放り込めば動く」でも、チーム開発や 6 ヶ月後の自分がメンテナンスするとなると話は別です。
この記事を読むと、クラスを「論理的に配置するための 3 つのルール」と「命名で迷わなくなる 5 パターン」をすぐに実践できるようになります。
さらに、GitHub で公開しているサンプルリポジトリを Eclipse/IntelliJ 両方でインポートして動かしながら、パッケージ分割のイメージを固められます。

前提知識

  • Java のクラス・インタフェースの違いを理解している
  • Maven もしくは Gradle のプロジェクト構成を一度は触ったことがある
  • コマンドラインで javac/java が叩ける(サンプル動作確認用)

なぜ「クラス管理」が問題になるのか

Java はファイル名=クラス名の言語であり、 1 ファイルに複数の public クラスを書けません。
そのため「クラスが増える=ファイルが増える=見つけにくくなる」という問題が必ず発生します。
さらに、パッケージ名を適当に付けると

  • 同じ用途のクラスが複数のパッケージに散在し、修正時にファイル探しで 5 分ロス
  • 名前が被るため FQN(完全限定名)での記述が増え、読みづらいコードに
  • デフォルトパッケージに置きっぱなしで、リフレクションやモジュールシステムで制限を受ける

といった「動くけど将来が怖いコード」が量産されます。
ここでは「論理単位でまとめ、命名規約で迷う時間をゼロにする」方法を考えます。

実践:パッケージ分割+命名規約を 30 分で終わらせる

ステップ1:ドメイン別 3 層パッケージを作る

  1. プロジェクトルートで mvn archetype:generate から myapp プロジェクトを作成
  2. src/main/java の直下に以下を作成
    - com.example.myapp.domain … エンティティ、値オブジェクト
    - com.example.myapp.application … ユースケース、サービス
    - com.example.myapp.infrastructure … DB アクセス、外部 API ラッパー

  3. 各層の public クラスは「層を跨ぐ参照」を禁止(Infrastructure → Domain のみ OK とする)
    これだけで「どこに置くか」が 90 % 決まります。

ステップ2:クラス名を「接尾辞パターン」で統一

命名で悩む時間を減らすため、以下の 5 接尾辞をルール化します。

役割 接尾辞例
エンティティ なし(名詞) User, Invoice
値オブジェクト Id, Date UserId, ExpiryDate
ファクトリ Factory UserFactory
リポジトリ実装 RepositoryImpl UserRepositoryImpl
インタフェース なし(上位に) UserRepository

これを IDE のファイルテンプレートに登録しておけば、クラス作成時に「どんな接尾辞?」で悩む必要がありません。

ステップ3:プライベート実装は「内部クラス」で隠す

public にするほどでもないヘルパーは、上位クラスの private static class として定義し、同一ファイルに閉じ込めます。
これで「クラス数が増える=必ずしもファイル数が増える」ではなくなり、ファイルリストが見やすくなります。

ハマった点:循環参照エラー

application 層の OrderServiceinfrastructure 層の UserRepositoryImpl を直接 new していたところ、以下のコンパイルエラーが出ました。

error: package com.example.myapp.infrastructure does not exist

原因は、applicationmodule-info.javarequires infrastructure; を書いていなかったこと。
ただし、クリーンアーキテクチャ的に「applicationinfrastructure を知ってはいけない」ため、根本解決として UserRepository インタフェースを domain 層に移動し、DI コンテナ(Spring や Dagger)で実装クラスを注入するようにしました。

解決策

  1. インタフェースを domain 層に移動
  2. application 側はインタフェースのみに依存
  3. 実装クラスは infrastructure 層に残し、DI 設定クラス(AppConfig)で結びつける

これで「コンパイルエラー」+「依存の向き」が同時に解決します。

まとめ

本記事では、Java で自作クラスが増えたときの「配置戦略」と「命名で迷わないルール」を紹介しました。

  • ドメイン別 3 層パッケージで「どこに置くか」を 90 % 自動化
  • 接尾辞パターンで「どんな名前にするか」を即決
  • 内部クラス & DI でファイル数と依存方向を整理

この記事を通して、クラスが 100 個を超えても「ファイル探し 5 分」が「即発見 5 秒」になるメリットを実感できるでしょう。
次回は、これらのパッケージを JPMS(Java Platform Module System)でモジュール化し、マルチモジュームビルド(Maven もしくは Gradle)を高速化する方法を紹介します。

参考資料