📘 Vol.10.8：Service層の共通基盤化とDI対応（テストもしやすく）

## ✅ 本記事の目的

本記事では、前回に続き **Service層の設計をさらに強化**し、以下を目指します：

- 複数のServiceで使える「共通基盤」の構築
- DI（依存性注入）を意識した柔軟でテストしやすい設計
- モック差し替えによるユニットテストの向上

---

## 🧩 1. なぜ共通基盤とDIが必要なのか？

### ✳️ 問題点（Before）

- Serviceクラスで同じような `DAO呼び出し` や `ログ出力`, `例外変換処理` が重複しやすい
- DAOを直に生成（new）しているため、テスト時に差し替えられない
- コードの再利用性・保守性が低下

### ✅ 解決策（After）

- 共通処理を `BaseService` や `AbstractService` にまとめる
- DAOやユーティリティを **DI（依存性注入）** で受け取る設計にする
- テスト時に **Mock DAO** へ差し替え可能に

---

## 🧩 2. Serviceインタフェース＋実装クラスの設計パターン

```java
// UserService.java（インタフェース）
public interface UserService {
    User getUserById(int id);
}
java
コピーする
編集する
// UserServiceImpl.java（実装クラス）
public class UserServiceImpl implements UserService {
    private final UserDao userDao;

    // DI（依存性注入）を意識したコンストラクタ
    public UserServiceImpl(UserDao userDao) {
        this.userDao = userDao;
    }

    @Override
    public User getUserById(int id) {
        return userDao.findById(id);
    }
}
```

---

### ✅ ポイント

- UserService インタフェースが外部公開契約

- UserServiceImpl は内部の具体実装

- DAOを「自分でnewせず、外から渡す（DI）」のがコツ

---

### 🧩 3. BaseServiceの共通処理を活用する

```java
// BaseService.java（共通処理を持たせる）
public abstract class BaseService {
    protected void log(String msg) {
        System.out.println("[Service Log] " + msg);
    }

    protected RuntimeException wrapException(Exception e) {
        return new RuntimeException("Service層例外：" + e.getMessage(), e);
    }
}

```

```java
// UserServiceImpl.java（継承して共通処理を活用）
public class UserServiceImpl extends BaseService implements UserService {
    private final UserDao userDao;

    public UserServiceImpl(UserDao userDao) {
        this.userDao = userDao;
    }

    @Override
    public User getUserById(int id) {
        try {
            log("ユーザー取得開始");
            return userDao.findById(id);
        } catch (Exception e) {
            throw wrapException(e);
        }
    }
}

```

---

### 🧪 4. DIによるユニットテストのしやすさ

```java
// UserServiceImplTest.java（JUnitでMock DAOを差し替え）
public class UserServiceImplTest {
    @Test
    public void testGetUserById() {
        // Mock DAOを渡す
        UserDao mockDao = new UserDao() {
            public User findById(int id) {
                User u = new User();
                u.setUserId(id);
                u.setUsername("mockUser");
                return u;
            }
            // 他のメソッドは未使用なので空実装でOK
        };

        UserService service = new UserServiceImpl(mockDao);

        User result = service.getUserById(1);
        assertEquals("mockUser", result.getUsername());
    }
}
```

### ✅ ポイント
- 実際のDBを使わずに動作検証ができる

- DIの設計にしておくことで、簡単にモック注入できる

---

### 🧩 5. DIフレームワークとの連携（将来的に）

💡 Spring導入時のイメージ

```java
@Service
public class UserServiceImpl implements UserService {
    @Autowired
    private UserDao userDao;

    ...
}

```

### ☑️ 将来Springなどを導入する際もDI設計にしておけば移行しやすい

---

### ✅ まとめ：Vol.10.x シリーズの設計がつながる

| 層       | 内容                                 |
| ------- | ---------------------------------- |
| DTO     | データの受け渡しモデル（Vol.10.1）              |
| DAO     | DBとの直接のやり取り（Vol.10.2～10.3）         |
| Action  | UI層との橋渡し（Vol.10.4）                 |
| Service | ロジック集中・DAO統合（Vol.10.6～10.8） ←いまここ！ |


---

### ✨ シリーズまとめ（Vol.10.x バリデーション編）

- [✅ Vol.10.0：DAO / DTO / JavaBeansとは？ ― データ層設計の全体像と役割分担 ―](https://qiita.com/drafts/19c1d526df28240e0cb1/edit)

- [✅ Vol.10.1：DTO設計のベストプラクティス（命名規則・フィールド設計](https://qiita.com/juehara-crypto/items/1b0ad616095f8ae134ec)

- [✅ Vol.10.2：DAOのインタフェース設計と実装パターン ](https://qiita.com/juehara-crypto/items/bcb15618440bef5931ef)

- [✅ Vol.10.3：DB接続クラスの共通化とドライバ設計](https://qiita.com/juehara-crypto/items/c3d4e8e6887d5ec8e1eb)

- [✅ Vol.10.4：Actionとの連携実装（Struts2との融合）](https://qiita.com/juehara-crypto/items/b8873de1aa9e46cd9309)

- [✅ Vol.10.5：DAO・DTOのテスト設計（JUnitやH2活用）](https://qiita.com/juehara-crypto/items/9f0914697726068b9e93)

- [✅ Vol.10.6：Service層の設計方針と例外処理戦略 ](https://qiita.com/juehara-crypto/items/6685c303c40e48b3c1f3)

- [✅ Vol.10.7：Service層×JUnitテストの基本とパターン](https://qiita.com/juehara-crypto/items/128fda40df3ffa0fedce)

---