web-dev-qa-db-ja.com

ドメインオブジェクトとそれらがデータベースにアクセスする方法

次のクラスがあるとします。

public class Course {
    // data
    public string Name { get; }
    public List<Student> Students {get;}
    //...

    // logic
    public int AverageGrade() {/* do something*/}
    public bool ReachedMaxStudents(){/* do something */}
}

クラスに生徒を挿入するなど、データベースとやり取りするいくつかの操作があります(データベースでも実行する必要があります)。これに対する最良の方法は何ですか?

最初のアプローチ:開発者はコースIDを知り、それをリポジトリに渡す必要があります。

public class CourseRepository : ICourseRepository{
    public void InsertStudent(int CourseId, StudentDto student){
        /* do something */
    }
}

2番目のアプローチ:ドメインオブジェクト内にリポジトリを埋め込む:

public class Course {
    private ICourseRepository _repo;

    // data
    public string Name { get; }
    public List<Student> Students {get;}
    //...

    // logic
    public int AverageGrade() {/* do something*/}
    public bool ReachedMaxStudents(){/* do something */}
    public void AddStudent(Student student){
        StudentDto s = /* map Student to Studentdto */
        _repo.Insert(s);
    }
}

各アプローチの長所と短所は何ですか?どちらが推奨されますか?

3
Husain

いいえ、ドメインオブジェクトは、リポジトリの存在や、リポジトリインターフェースさえも把握していないはずです。これを行う必要があると感じた場合は、設計に根本的な問題があることを意味します(以下を参照)。

問題の原因は、すべてのエンティティを適切にモデル化していないことです。

コースには学生がいません登録があります

存在しないエンティティが問題を引き起こしているだけでなく、オブジェクトが永続化する方法を知っている必要があると感じている場合(そうではありません)。 Edgeの状況で問題が発生します...たとえば、学生がコースに登録した後、登録をキャンセルした後、再度気が変わってコースに再登録するとどうなりますか?現在のモデルは、何らかの履歴分析を行う必要がある場合、この状況を処理できません(おそらく、キャンセル料が発生する可能性があります)。

ここには少なくとも6つの異なるクラスが必要です:RegistrationStudentCourseRegistrationRepositoryStudentRepository、およびCourseRepository

リポジトリが直接のエンティティのみを保存しようとすることを確認してください... CourseRepositoryがすべてのコース登録を保存しようとしないでください。

あなたのコードはこのようなものを望んでいます...

//This would be a method in the UI, maybe called when a button is clicked... ect
void UI_RegisterStudentForCourse(Student studentToReg, int courseId)
{
    //Register student for course
    Course course = courseRepo.GetCourse(courseId);
    course.Registrations = regRepo.GetStudents(courseId);

    Registration reg = new Registration();
    reg.Course = course;
    reg.Student = studentToReg;
    reg.Date = DateTime.Now;

    if (course.CanRegister(reg)) // Check if the course is full, ect
       regRepo.Save(reg);
    else
       //Tell the user this registration could not be completed
}

リストした2番目のアプローチの問題は、コードが非常に脆弱になることです。保存しないでシミュレーションを実行する必要がある場合はどうなりますか?登録ロジックに対してユニットテストを実行したい場合はどうなりますか?それは大規模なモックを必要とするでしょう。さらに、コードが単一責任の原則(この場合、保存とドメインロジックを混在させる)に違反していると、保守が困難になり、コードの変更がはるかに危険になります。

8
TheCatWhisperer