質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

89.86%

DaoとRepositoryの使い方

解決済

回答 1

投稿

  • 評価
  • クリップ 1
  • VIEW 2,884

sanezane

score 78

Spring bootにてWebアプリ開発を勉強中です。Domaでのデータアクセスをしようと思った時に疑問が出たので質問します。

参考にしているプロジェクト内でDaoとRepositoryを分けているプロジェクトを見つけたため何が違うのかと調べていたのですが、イマイチ理解できないでいます。

参考にしているプロジェクト

その中でDaoとRepositoryを分けている箇所は以下のような実装をしています。

package com.sample.domain.dao.users;

import java.util.List;
import java.util.Optional;
import java.util.stream.Collector;

import org.seasar.doma.*;
import org.seasar.doma.boot.ConfigAutowireable;
import org.seasar.doma.jdbc.SelectOptions;

import com.sample.domain.dto.user.User;
import com.sample.domain.dto.user.UserCriteria;

@ConfigAutowireable
@Dao
public interface UserDao {

    /**
     * ユーザーを取得します。
     *
     * @param criteria
     * @param options
     * @return
     */
    @Select(strategy = SelectType.COLLECT)
    <R> R selectAll(final UserCriteria criteria, final SelectOptions options, final Collector<User, ?, R> collector);

    /**
     * ユーザーを1件取得します。
     *
     * @param id
     * @return
     */
    @Select
    Optional<User> selectById(Long id);

    /**
     * ユーザーを1件取得します。
     *
     * @param criteria
     * @return
     */
    @Select
    Optional<User> select(UserCriteria criteria);

    /**
     * ユーザーを登録します。
     *
     * @param user
     * @return
     */
    @Insert
    int insert(User user);

    /**
     * ユーザーを更新します。
     *
     * @param user
     * @return
     */
    @Update
    int update(User user);

    /**
     * ユーザーを論理削除します。
     *
     * @param user
     * @return
     */
    @Update(excludeNull = true) // NULLの項目は更新対象にしない
    int delete(User user);

    /**
     * ユーザーを一括登録します。
     *
     * @param users
     * @return
     */
    @BatchInsert
    int[] insert(List<User> users);

    /**
     * ユーザーを一括更新します。
     *
     * @param users
     * @return
     */
    @BatchUpdate
    int[] update(List<User> users);
}
package com.sample.domain.repository.users;

import static com.sample.domain.util.DomaUtils.createSelectOptions;
import static java.util.Optional.ofNullable;
import static java.util.stream.Collectors.toList;

import java.util.Optional;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import com.sample.domain.dao.system.UploadFileDao;
import com.sample.domain.dao.users.UserDao;
import com.sample.domain.dao.users.UserRoleDao;
import com.sample.domain.dto.common.Page;
import com.sample.domain.dto.common.Pageable;
import com.sample.domain.dto.user.User;
import com.sample.domain.dto.user.UserCriteria;
import com.sample.domain.dto.user.UserRole;
import com.sample.domain.exception.NoDataFoundException;
import com.sample.domain.service.BaseRepository;

import lombok.val;

@Repository
public class UserRepository extends BaseRepository {

    @Autowired
    UserDao userDao;

    @Autowired
    UserRoleDao userRoleDao;

    @Autowired
    UploadFileDao uploadFileDao;

    /**
     * ユーザーを取得します。
     * 
     * @param criteria
     * @param pageable
     * @return
     */
    public Page<User> findAll(UserCriteria criteria, Pageable pageable) {
        // ページングを指定する
        val options = createSelectOptions(pageable).count();
        val data = userDao.selectAll(criteria, options, toList());
        return pageFactory.create(data, pageable, options.getCount());
    }

    /**
     * ユーザーを取得します。
     * 
     * @param criteria
     * @return
     */
    public Optional<User> findOne(UserCriteria criteria) {
        // 1件取得
        val user = userDao.select(criteria);

        // 添付ファイルを取得する
        user.ifPresent(u -> {
            val uploadFileId = u.getUploadFileId();
            val uploadFile = ofNullable(uploadFileId).map(uploadFileDao::selectById);
            uploadFile.ifPresent(u::setUploadFile);
        });

        return user;
    }

    /**
     * ユーザー取得します。
     *
     * @return
     */
    public User findById(final Long id) {
        return userDao.selectById(id).orElseThrow(() -> new NoDataFoundException("user_id=" + id + " のデータが見つかりません。"));
    }

    /**
     * ユーザーを追加します。
     *
     * @param inputUser
     * @return
     */
    public User create(final User inputUser) {

        // 1件登録
        userDao.insert(inputUser);

        // 役割権限紐付けを登録する
        val userRole = new UserRole();
        userRole.setUserId(inputUser.getId());
        userRole.setRoleKey("users");
        userRoleDao.insert(userRole);

        return inputUser;
    }

    /**
     * ユーザーを更新します。
     *
     * @param inputUser
     * @return
     */
    public User update(final User inputUser) {

        val uploadFile = inputUser.getUploadFile();
        if (uploadFile != null) {
            // 添付ファイルがある場合は、登録・更新する
            val uploadFileId = inputUser.getUploadFileId();
            if (uploadFileId == null) {
                uploadFileDao.insert(uploadFile);
            } else {
                uploadFileDao.update(uploadFile);
            }

            inputUser.setUploadFileId(uploadFile.getId());
        }

        // 1件更新
        int updated = userDao.update(inputUser);

        if (updated < 1) {
            throw new NoDataFoundException("user_id=" + inputUser.getId() + " のデータが見つかりません。");
        }

        return inputUser;
    }

    /**
     * ユーザーを論理削除します。
     *
     * @return
     */
    public User delete(final Long id) {
        val user = userDao.selectById(id)
                .orElseThrow(() -> new NoDataFoundException("user_id=" + id + " のデータが見つかりません。"));

        int updated = userDao.delete(user);

        if (updated < 1) {
            throw new NoDataFoundException("user_id=" + id + " は更新できませんでした。");
        }

        return user;
    }
}

このようにRepositoryにてDaoをAutoweredしてメソッドを使用しています。

私の中のDaoとRepository

私の中で2つは「デザインパターンレベルでのデータを取得したり、操作するもの」という認識で、大きな違いはないと理解していました。
ただ今回使い分けているんだからなんか違うんだろう、そして同じプロジェクトで同じ階層で使用すべきものなのだろうと思って調査をしました。

1.DaoとRepositoryのすみ分け

Repositoryはドメインオブジェクトを見て、DAOはデータ・ソースのマッピングするDTOの方を見る


「ベクトルが違うのはなんとなく伝わるけどまだわからない」
次!!!

2.リポジトリとDaoの住み分け方より

・リポジトリはドメインオブジェクトのコレクション
・DAOはデータアクセスのメカニズムを隠蔽する

・リポジトリの実装はドメインオブジェクトの構築です。


んー、頭の中で噛み砕けない。。。
恥ずかしながら私はデザインパターンを学んだ事がなく、こういう話は理解するのに苦労します。

SESの世界に例えた時に、簡単に言うと「二次請け企業と三次請け企業」のようにレイヤーが異なるという事でしょうか?
もう少し抽象的で構わないので子供に理解できるレベルの日本語で解説するとしたらどのような感じになりますでしょうか?
ご意見をよろしくお願いします。
また、皆さんの現場ではDaoとRepositoryは分けられていますか?

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • wwbQzhMkhhgEmhU

    2019/02/17 12:35

    ざっくり↓なイメージですが、特別両者を同じような位置付けで見たことがなかったです。
    DAO=データベース周り
    Repository=それ以外
    日本でリポジトリという言葉が使われ始めたのは、2000年くらいからだと思いますが、以来〇〇リポジトリでしっくり来る箱のときはリポジトリと呼んでる感じです。私が初めて知ったのはsvnを触ったときでした。ユーザーリポジトリはしっくり来る感じですし、Springでもそういうのあった気がします。
    私はOO〜最近巡り巡って何故かまたDDDまでほとんど知らないので、使い方は適当ですけどね。

    キャンセル

  • sanezane

    2019/02/17 14:53

    なるほど。私はSESで、開発案件としてまだ2件しか参画していませんでしたので、普通はどういう使い方をしているのか気になりました。
    もう少しデザインパターンについて勉強したいと思います。

    キャンセル

  • A-pZ

    2019/02/19 04:15

    回答は解答欄に入力しましょう

    キャンセル

回答 1

checkベストアンサー

+2

SpringFrameworkが提案するRepositoryは、ドメイン駆動デザインのRepositoryとして用意され、JavaEEのDataAccessObject(DAO)と全く同じ扱いにもできますが、RepositoryはDAOよりも抽象度が高く、Repositoryを利用する側からは、どのデータストアに対して検索や更新しているかを意識せずに利用できるように設計・実装されるべき、とされています。

例えば何らかの検索機能を提供する際に、RDBから検索してした機能以外にも他のストレージやWebサービスへ切り替えるときに、これらを直接検索している処理はDAOで行わせ、DAOを切り替えることでRepositoryは常に期待する結果を返却するインタフェースを提供します。

https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/stereotype/Repository.html

もちろん実際にDAOが扱うデータストアが単一の場合も多いでしょうから、Repository=DAOとしてしまうケースも多いでしょうし、DAOで利用するフレームワークを使うと、よりさらに違いがないと感じられる場面も多いでしょう。

アンチパターンも含めて、こちらに具体的な、砕けた例も書かれていますので参考になれば幸いです。

https://qiita.com/mikesorae/items/ff8192fb9cf106262dbf

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/03/06 00:04

    Repositoryは永続化する機能に絞って複雑な条件の処理はServiceで実装する。
    アンチパターンに気をつけて過ごします。
    Qiitaの記事わかりやすかったです。ありがとうございます。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 89.86%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

同じタグがついた質問を見る