Spring BootでのDAOの設計:適切な切り分け方(新人エンジニア向け)

こんにちは。ゆうせいです。
Spring Boot でWebアプリケーションを作るとき、
「DAO(Data Access Object)ってどうやって設計すればいいの?」 と思うことがありますよね。

DAOは何のために必要?
DAOをどこまで作るべき?
DAOをどう切り分ければ、メンテナンスしやすい設計になる?

結論:
DAOは「データベースとのやりとり専用」にし、@Repository を使って管理するのが基本!
今回は、新人エンジニア向けに、Spring BootのDAOの適切な設計方法 をわかりやすく解説します!


1. DAO(Data Access Object)とは?

DAO(データアクセスオブジェクト)は、
データベースとのやりとりを行うクラス(レイヤー) です。

📌 DAOの役割

  1. データベースへの接続(CRUD操作)
  2. SQLを実行してデータを取得・更新
  3. アプリケーションの他の部分(Service や Controller)からSQLの記述を隠蔽

2. DAOを使わないとどうなる?(悪い例)

DAOを使わずに、コントローラーやサービス層に直接SQLを書いてしまう のは避けるべきです。

@RestController
@RequestMapping("/users")
public class UserController {

    private final JdbcTemplate jdbcTemplate;

    public UserController(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    @GetMapping("/{id}")
    public User getUser(@PathVariable Long id) {
        String sql = "SELECT * FROM users WHERE id = ?";
        return jdbcTemplate.queryForObject(sql, new Object[]{id}, new BeanPropertyRowMapper<>(User.class));
    }
}

❌ この設計の問題点

  1. SQLがControllerやServiceに直接書かれると、コードが汚れる
  2. データベースの処理が散らばり、修正が難しくなる
  3. テストがしにくい(データアクセスの処理をモックできない)

📌 DAOを使うことで、SQLを専用クラスに分離し、責務を明確にできる!


3. DAOの適切な設計

✅ DAOの基本的な構成

クラス役割
DAO(UserDao など)データベースアクセス(SQLの実行)
Entity(Userデータベースの1行を表す
Repository(UserRepositorySpring Data JPA を利用したDB操作
Service(UserServiceビジネスロジック

📌 DAO は @Repository を付けて、データアクセス専用のレイヤーとして設計する!


✅ 適切なDAOの作り方(JDBC版)

1. エンティティ(User

public class User {
    private Long id;
    private String username;
    private String email;

    // ゲッター・セッター
}

📌 データベースの1行を表すクラス


2. DAOインターフェース(UserDao

public interface UserDao {
    User findById(Long id);
    List<User> findAll();
    void save(User user);
    void update(User user);
    void delete(Long id);
}

📌 DAOインターフェースを定義し、実装クラスを作る


3. DAO実装(UserDaoImpl

@Repository
public class UserDaoImpl implements UserDao {

    private final JdbcTemplate jdbcTemplate;

    public UserDaoImpl(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    @Override
    public User findById(Long id) {
        String sql = "SELECT * FROM users WHERE id = ?";
        return jdbcTemplate.queryForObject(sql, new Object[]{id}, new BeanPropertyRowMapper<>(User.class));
    }

    @Override
    public List<User> findAll() {
        String sql = "SELECT * FROM users";
        return jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(User.class));
    }

    @Override
    public void save(User user) {
        String sql = "INSERT INTO users (username, email) VALUES (?, ?)";
        jdbcTemplate.update(sql, user.getUsername(), user.getEmail());
    }

    @Override
    public void update(User user) {
        String sql = "UPDATE users SET username = ?, email = ? WHERE id = ?";
        jdbcTemplate.update(sql, user.getUsername(), user.getEmail(), user.getId());
    }

    @Override
    public void delete(Long id) {
        String sql = "DELETE FROM users WHERE id = ?";
        jdbcTemplate.update(sql, id);
    }
}

📌 JdbcTemplate を使ってSQLを実行し、データを取得・更新する


4. サービス層でDAOを利用(UserService

@Service
public class UserService {
    
    private final UserDao userDao;

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

    public User getUserById(Long id) {
        return userDao.findById(id);
    }

    public List<User> getAllUsers() {
        return userDao.findAll();
    }

    public void createUser(User user) {
        userDao.save(user);
    }

    public void updateUser(User user) {
        userDao.update(user);
    }

    public void deleteUser(Long id) {
        userDao.delete(id);
    }
}

📌 Service層ではDAOを呼び出してビジネスロジックを実行!


5. コントローラーでサービスを利用(UserController

@RestController
@RequestMapping("/users")
public class UserController {
    
    private final UserService userService;

    public UserController(UserService userService) {
        this.userService = userService;
    }

    @GetMapping("/{id}")
    public User getUser(@PathVariable Long id) {
        return userService.getUserById(id);
    }

    @GetMapping
    public List<User> getAllUsers() {
        return userService.getAllUsers();
    }

    @PostMapping
    public void createUser(@RequestBody User user) {
        userService.createUser(user);
    }

    @PutMapping("/{id}")
    public void updateUser(@PathVariable Long id, @RequestBody User user) {
        user.setId(id);
        userService.updateUser(user);
    }

    @DeleteMapping("/{id}")
    public void deleteUser(@PathVariable Long id) {
        userService.deleteUser(id);
    }
}

📌 Controller層では @Service を呼び出して、データを取得・更新する!


4. まとめ

設計のポイント説明
DAOはデータアクセス専用にするSQLの記述をDAOに集約し、他のレイヤーに影響を与えない
DAOは@Repository を付けるSpringが自動的にDI管理してくれる
DAOのインターフェースを作る実装を変更しやすくなる(JPAや別DBへの移行がスムーズ)
Controller → Service → DAO の流れを守るコントローラーでは直接DAOを呼び出さない

5. 結論

🚀 DAOは「データベースとのやりとり専用」にし、SQLの実行を分離!
🚀 DAOのインターフェースを作ることで、実装の変更がしやすくなる!
🚀 Controller → Service → DAO の流れを守り、レイヤーごとに役割を明確にする!

この設計を守れば、「拡張しやすく、メンテナンスしやすいSpring Bootアプリ」 を作れます!
ぜひ実践してみてください!ことが大切!
これを意識して、エンジニアが自発的に学べる環境を作っていきましょう!

セイ・コンサルティング・グループでは新人エンジニア研修のアシスタント講師を募集しています。

投稿者プロフィール

山崎講師
山崎講師代表取締役
セイ・コンサルティング・グループ株式会社代表取締役。
岐阜県出身。
2000年創業、2004年会社設立。
IT企業向け人材育成研修歴業界歴20年以上。
すべての無駄を省いた費用対効果の高い「筋肉質」な研修を提供します!
この記事に間違い等ありましたらぜひお知らせください。