Search code examples
c#domain-driven-designddd-repositories

Where to access persistance in DDD


I am looking into Domain Driven Design (yes, I am quite late for that party) and so far I have come to the realization that the Domain Model should be the center of the universe. The data persistance is merely a practical detail.

However, if this is true, then I have a hard time figuring out where the communication with the persititance part (e.g. a repository) should be.

For instance, if I want to be able to add a new grade to a student, should I call the repository from inside the student domain model, like this?

interface IGradeRepository
{
    void SaveGrade(int courseId, string grade);
    // ...other methods
}

class Student
{
    IGradeRepository _gradeRepository;
    List<Grade> _grades = new List<Grade>();

    public Student(IGradeRepository gradeRepository) 
    {
        _gradeRepository = gradeRepository;
    }

    int StudentId { get; set; }

    void AddGrade(int courseId, string grade)
    {
        var grade = new Grade(this.StudentId, courseId, grade);
        _grades.Add(grade);

        // Do I put the call to the data persistance here?
        _gradeRepository.SaveGrade(grade);
    }
}

As you can see, my best bet is to access the repository from inside the domain model. However, I am not sure if this is the way to go about it. It seems to be one of the things that is left out in many tutorials.

So to summarize: From where do I access the data layer, when doing Domain Driven Design?

Update

As some have commented, it is not clear from my example, how I would access the student class. I would probably do that from a view model or through some sort of use case service.

Example with view model:

class StudentGradesViewModel
{
    // (...) all sort of VM-stuff

    private Student _student;
    private Course _selectedCourse;

    public void AddGrade(string grade)
    {
        _student.AddGrade(_course.CourseId, grade);
    }
}

Solution

  • First of all: The repository should only refer to aggregate. In your case, you have student which can have multiple grades. So, you repository should always be the persistance for the aggregate, not an entity inside an aggregate.

    Here is the solution with the way I would choose:

    Aggregate in Domain Layer:

    class Student
    {
        List<Grade> _grades = new List<Grade>();
        int StudentId { get; set; }
    
        public Student() 
        {
        }
    
        void AddGrade(int courseId, string grade)
        {
            var grade = new Grade(this.StudentId, courseId, grade);
            _grades.Add(grade);
        }
    }
    

    Repository Interface in Domain Layer:

    interface IStudentRepository
    {
        void SaveStudent(int studentId);
        // ...other methods
    }
    

    Of course, as it was mentioned in posts above, the implementation of Repository should take place infrastructure layer.

    You should create Application Service or CommandHandler (if you're using CQRS) in order to integrate domain logic and infrstructure:

    class AddGradeService
    {    
        private readonly IStudentRepository _studentRepository;
    
        public AddGradeService(IStudentRepository studentRepository) 
        {
            _studentRepository = studentRepository;
        }
    
        void Handle(int studentId, int courseId, string grade)
        {
            Student student = _studentRepository.Get(studentId);
            student.AddGrade(courseId, grade);
            _studentRepository.Save(student);
        }
    }
    

    This is how it should be done :) You can also refer to my example od DDD if you want: https://github.com/czarek-szok/DDD.Ecommerce/