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

How to figure out which repository to call for different implementations of an interface?


I am just starting in DDD and have a question regarding interfaces of objects and repositories. Suppose I have the following objects

public interface IPerson { ... }

public class Student 
{
    double gpa;
    ...
}

public class Teacher
{
    double salary; ...
}

then I also have two repositories such as

public class StudentRepository :IRepository { public void Save(Student) }

public class TeacherRepository :IRepository { public void Save(Teacher) }

My question is, suppose I have a list of IPerson objects called persons, is there a way where I can just do something like repository.Save(persons) ? Without having to use reflection to figure out what type the IPerson actually is.

I currently have another class

PersonRepository :IRepository 
{
     public void Save(IPerson person)
     {
          if(Person is Student)
          {
               new StudentRepository.Save(person as Student);
          }
          else if(Person is Teacher)
          { ....}
      }
}

Then I can call personRepository.Save(persons); However this doesnt feel like an optimal way to structure things. How can I improve this design?

Thanks

EDIT:

What I'm looking for is, say I receive an IPerson object called person. I do not necessarily know what implementation it is, I just want to call repository.Save(person) and have it call the correct repository. Is there a way to do this without using some sort of switch statement with reflection?


Solution

  • Consider using generic repository

    class Repository<T> :IRepository<T>
    {
         public void Save(T entity)
         {
             ...
         }
    }
    

    Usage

    IRepository<Student> repo1 = new Repository<Student>();
    repo1.Save(new Student());
    
    IRepository<Teacher> repo2 = new Repository<Teacher>();
    repo2.Save(new Teacher());
    


    Next you can use IoC container and DI just to pass repositories around instead of creating them

    At the top level, say in the main method or global.asax

    IRepository<Student> studentRepo = IoC.Current.Resolve<IRepository<Student>>();
    

    Later in a class that needs to save data, pass IRepository<Student> studentRepo into constructor

    class Foo
    {
        private IRepository<Student> repo
    
        Foo(IRepository<Student> repo)
        {
            this.repo = repo;
        }
    
        public void Save(Student s)
        {
            repo.Save(s);
        }
    }
    

    EDIT

    You can move a save operation to the IPerson<T>

    class Person<T> : IPerson<T>
    {
        private IRepository<T> repo;
    
        Person(IRepository<T> repo)
        {
            this.repo = repo;
        }
    
        public void Save()
        {
            repo.Save<T>();
        }
    }
    

    So when you derive Teacher and Student from Person<T> you pass correspondent T, like

    class Student : Person<Student>
    {
        private IRepository<Student> repo;
    
        Person(IRepository<Student> repo):base(repo)
        {
           ...
        }    
    }
    

    This shall give you the ability to work with List without Reflection or switch kung fu.