Spring Bootを用いたWebアプリケーション開発では、保守性と拡張性を高めるために「3層アーキテクチャ(3-tier Architecture)」を採用するのが一般的です。この設計パターンでは、役割に応じて処理をController層、Service層、Data Access層(Repository/DAO)の3つに分離します。
3層アーキテクチャの役割
- Controller層: 外部からのHTTPリクエストを受け付け、レスポンスを返却する役割を担います。ビジネスロジックは含まず、適切なServiceを呼び出すことに専念します。
- Service層: アプリケーションの主要なビジネスロジックを記述します。データの加工や計算、トランザクションの管理などを行います。
- Data Access層 (DAO/Repository): データベースやファイルなどの外部リソースとのデータのやり取りを抽象化します。
IoCとDIによる疎結合の実現
各層のコンポーネントを直接 new 演算子でインスタンス化すると、クラス間の結合度が強くなり、テストや仕様変更が困難になります。Spring Frameworkでは、IoC(制御の逆転)コンテナがオブジェクトの生成と管理を行い、DI(依存性の注入)によって必要なインスタンスを注入することで、この問題を解決します。
1. Service層のインターフェース定義
実装詳細に依存させないため、まずインターフェースを定義します。
package com.example.demo.service;
import com.example.demo.model.Staff;
import java.util.List;
public interface StaffService {
List<Staff> getFormattedStaffList();
}
2. Serviceの実装クラスとBean登録
@Service アノテーションを付与することで、SpringのIoCコンテナに管理対象(Bean)として登録されます。また、DAO層への依存も @Autowired で注入します。
package com.example.demo.service.impl;
import com.example.demo.repository.StaffRepository;
import com.example.demo.model.Staff;
import com.example.demo.service.StaffService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class StaffServiceImpl implements StaffService {
@Autowired
private StaffRepository staffRepository;
@Override
public List<Staff> getFormattedStaffList() {
List<Staff> staffList = staffRepository.findAll();
// ビジネスロジック:コード値を表示用ラベルに変換
staffList.forEach(staff -> {
// 性別の変換
staff.setGenderName("1".equals(staff.getGenderCode()) ? "男性" : "女性");
// 役職の変換
String role = staff.getRoleCode();
switch (role) {
case "1": staff.setRoleName("エンジニア"); break;
case "2": staff.setRoleName("デザイナー"); break;
case "3": staff.setRoleName("マネージャー"); break;
default: staff.setRoleName("未設定");
}
});
return staffList;
}
}
3. Controller層でのインジェクション
Controllerでは特定のクラス名(StaffServiceImpl)を指定するのではなく、インターフェース(StaffService)型に対して注入を行います。これにより、実装が変更されてもController側のコードを修正する必要がなくなります。
package com.example.demo.controller;
import com.example.demo.model.Result;
import com.example.demo.model.Staff;
import com.example.demo.service.StaffService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
public class StaffController {
@Autowired
private StaffService staffService;
@GetMapping("/api/staff")
public Result list() {
// Service層から加工済みのデータを取得
List<Staff> data = staffService.getFormattedStaffList();
return Result.success(data);
}
}
アノテーションの使い分け
Spring Bootでは、用途に応じて以下のステレオタイプアノテーションを使用します。
@Component: 汎用的なコンポーネントを定義します。@Service: ビジネスロジックを持つService層であることを明示します。@Repository: Data Access層(DAO)であることを明示し、DB特有の例外変換などの機能を提供します。@RestController: Web APIのコントローラーであることを明示します。
これらのアノテーションと @Autowired を適切に使用することで、各コンポーネントが疎結合に保たれ、メンテナンス性の高いアプリケーションを構築することが可能になります。さらに、複数の実装が存在する場合は @Qualifier や @Resource を用いて注入するBeanを明示的に指定することも可能です。