Spring Bootにおける3層レイヤー設計と依存性の注入(DI)による疎結合の実装

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を明示的に指定することも可能です。

タグ: Spring Boot Java Dependency Injection IoC Software Architecture

8月14日 14:22 投稿