Oggetti dominio e modalità di accesso al database

3

Supponiamo di avere la seguente classe:

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

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

Ho alcune operazioni che interagiscono con il database come l'inserimento di uno studente in una classe (dovrebbe essere fatto anche nel database). Qual è l'approccio migliore a questo?

Primo approccio: lo sviluppatore deve conoscere l'ID del corso e passarlo al repository.

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

Secondo approccio: repository incorporato all'interno dell'oggetto dominio:

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);
    }
}

Quali sono i pro e i contro per ciascun approccio e quale è il preferito?

    
posta Husain 05.10.2017 - 21:46
fonte

1 risposta

6

No, gli oggetti di dominio non dovrebbero avere alcuna idea dell'esistenza di un repository, o anche di un'interfaccia di repository. Se senti la necessità di farlo, significa che c'è un problema di fondo con il tuo design (vedi sotto).

La radice del tuo problema è che non hai modellato correttamente tutte le tue entità.

Un corso non ha studenti ha registrazioni .

Non solo l'entità mancante causa i tuoi problemi dove ritieni che gli oggetti debbano sapere come persistere (non lo fanno); ti causerà problemi in situazioni marginali ... Ad esempio, cosa succede se il tuo studente si iscrive a un corso, quindi annulla la registrazione, ma poi cambia idea e si iscrive nuovamente a un corso? Il tuo modello attuale non può gestire questa situazione se deve effettuare analisi storiche (forse c'è una tassa di cancellazione che deve essere prelevata).

Hai bisogno di almeno sei classi diverse qui: Registration , Student , Course , RegistrationRepository , StudentRepository e CourseRepository .

Assicurati che i tuoi repository provino solo a salvare la sua entità diretta ... non abbia il CourseRepository a provare a salvare tutte le registrazioni dei corsi.

Il tuo codice vorrebbe qualcosa di simile ...

//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
}

Il problema con il secondo approccio che hai elencato è che rende il tuo codice molto fragile. Che cosa succede se è necessario eseguire una simulazione in cui le cose non dovrebbero essere salvate? Cosa succede se si desidera eseguire test unitari contro la logica di registrazione? Richiederebbe estenuanti prese in giro. Inoltre, quando il tuo codice viola il principio di Single Responsibility (mixaggio del salvataggio con la logica del dominio in questo caso), rende le cose difficili da mantenere e le modifiche al codice sono molto più rischiose.

    
risposta data 05.10.2017 - 23:42
fonte