0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Spring Boot コンストラクタ使い分け_Command vs Service の設計パターン

Last updated at Posted at 2025-09-02

はじめに

Spring Boot 開発において、「なぜ Command クラスにはデフォルトコンストラクタが必要で、Service クラスには不要なのか?」という疑問を持ったことはありませんか?

この記事では、Spring Boot の DI(依存性注入)と JSON デシリアライゼーションの仕組みを理解し、適切なコンストラクタ設計パターンを解説します。

🎯 核心:二つの異なるインスタンス化方式

Command/DTO クラス:JSON 変換によるインスタンス化

// HTTPリクエスト → JSON → Javaオブジェクト
@PostMapping("/register")
public ResponseEntity<?> register(@RequestBody RegisterUserCommand command) {
    // ↑ Jacksonライブラリが自動変換
}

Service クラス:Spring DI によるインスタンス化

// Springコンテナが直接管理・注入
@Service
public class RegisterUserService {
    // Spring DIが直接呼び出し
    public RegisterUserService(UserRepository userRepository) { ... }
}

📋 実装パターン比較

❌ 間違った理解

「Service クラスにもデフォルトコンストラクタが必要」

✅ 正しい理解

「用途によってコンストラクタの要件が異なる」

🔍 詳細解説

1. Command クラスの設計パターン

/**
 * ユーザー登録コマンド
 * HTTPリクエストボディからの自動変換が必要
 */
public class RegisterUserCommand {
    @NotNull
    private String email;

    @NotNull
    private String password;

    @NotNull
    private String username;

    /**
     * デフォルトコンストラクタ(Jackson用)
     * JSON → Java変換時に必須
     */
    public RegisterUserCommand() {}

    /**
     * 全項目指定コンストラクタ(テスト用)
     */
    public RegisterUserCommand(String email, String password, String username) {
        this.email = email;
        this.password = password;
        this.username = username;
    }

    // getter/setter必須(Jackson用)
    public String getEmail() { return email; }
    public void setEmail(String email) { this.email = email; }
    // 他のgetter/setterも同様...
}

なぜデフォルトコンストラクタが必要?

  • Jackson ライブラリが JSON から Java オブジェクトに変換する際の仕組み
  • Jackson 変換プロセス:new Command()setEmail()setPassword()setUsername()
  • デフォルトコンストラクタ + setter のパターンが標準

2. Service クラスの設計パターン

/**
 * ユーザー登録サービス
 * Spring DIによる依存性注入
 */
@Service
@Transactional
public class RegisterUserService {
    private final UserRepository userRepository;

    /**
     * コンストラクタベースDI(推奨パターン)
     * Spring Boot 2.0以降は@Autowired不要
     */
    public RegisterUserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    /**
     * ビジネスロジック実装
     */
    public User register(RegisterUserCommand command) {
        // 重複チェック、ドメインモデル生成、永続化
        // ...
    }
}

なぜデフォルトコンストラクタが不要?

  • Spring コンテナが直接インスタンス化
  • 外部からのデータ変換処理は発生しない
  • 必要な依存関係は Spring が解決済み
  • 不変オブジェクトとして設計可能(final フィールド)

🏗️ Spring Boot 2.0 以降の自動 DI 条件

以下の条件を満たすとコンストラクタベース DI が自動で動作:

  1. ✅ Spring アノテーション(@Service, @Componentなど)が付与
  2. ✅ コンストラクタが1 つだけ存在
  3. ✅ 引数が全て Spring の Bean として登録済み
// ❌ 複数コンストラクタ存在時は@Autowired必要
@Service
public class BadExample {
    private final UserRepository userRepository;

    public BadExample() { ... }  // デフォルトコンストラクタ

    @Autowired  // 必須!
    public BadExample(UserRepository userRepository) { ... }
}

📊 使い分けルール一覧

クラス種別 デフォルトコンストラクタ 理由 実装パターン
Command/DTO 必須 JSON ↔ Java 変換 デフォルト + 全項目
Service 不要 Spring DI 管理 コンストラクタベース DI
Repository 実装 不要 Spring DI 管理 コンストラクタベース DI
JPA Entity 必須 JPA/Hibernate 要件 protected デフォルト
Value Object 不要 不変オブジェクト 引数必須コンストラクタ

🔧 実践的な設計指針

1. Command クラス設計時のチェックリスト

  • デフォルトコンストラクタの実装
  • 全項目指定コンストラクタの実装(テスト用)
  • getter/setter の実装(Jackson 用)
  • バリデーションアノテーションの設定
  • ドメインオブジェクト変換メソッド(toEmail()など)

2. Service クラス設計時のチェックリスト

  • final フィールドによる不変性確保
  • コンストラクタ引数の最小化
  • 単一責任原則の遵守
  • トランザクション境界の明確化
  • 適切な Javadoc 記載

🚨 よくある間違いパターン

間違い 1:Service クラスにデフォルトコンストラクタを追加

// ❌ 不要なデフォルトコンストラクタ
@Service
public class RegisterUserService {
    private UserRepository userRepository;

    public RegisterUserService() {}  // 不要!

    public RegisterUserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }
}

間違い 2:Command クラスでコンストラクタベースのみ

// ❌ デフォルトコンストラクタなし
public class RegisterUserCommand {
    private final String email;  // JSON変換できない!

    // デフォルトコンストラクタがない
    public RegisterUserCommand(String email, String password, String username) {
        this.email = email;
        // ...
    }
}

🎯 まとめ

Spring Boot におけるコンストラクタ設計は、インスタンス化の方式によって決まります:

  • JSON 変換が必要なクラス → デフォルトコンストラクタ + setter
  • Spring DI が管理するクラス → コンストラクタベース DI

この違いを理解することで、適切な設計パターンを選択し、保守性の高い Spring Boot アプリケーションを構築できます。

参考資料


この記事が Spring Boot でのコンストラクタ設計の理解に役立てば幸いです!

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?