views:

66

answers:

2

This time I have a more philosopical question.

Most MVC tutorials/books seem to suggest to restrict the scope of one repository to one aspect of the model and set up multiple repositories to cover all model classes. (E.g.: ProjectRep, UserRep, ImageRep, all mapping onto the same db eventually.)

I can see how that would make unittesting simpler but I can't imagine how this would work in the real world, where most entities have relationships between each other. In the end I always find myself with one gigantic repository class per DB connection and an equally arkward FakeRepository for unittesting.

So, what's your opinion? Should I try harder to seperate out repositories? Does it even matter if the ProductRep refers to data in the UserRep and vice versa via PurchaseHistory? How would the different reps make sure they don't lock each other up when accessing the single db?

Thanks, Duffy

+2  A: 

I've found that by using generics and an interface, you can avoid having to code many separate repositories. If your domain model is well-factored, you can often navigate object graphs using the domain objects rather than another repository.

public class MyDomainObject : IEntity //IEntity is an arbitrary interface for base ents
{
     public int Id { get; set;}
     public List<DiffObj> NavigationProp { get; set;}
     //... and so on...
}

public interface IRepository<T> where T : IEntity, class, new()
{
     void Inert(T entity);
     T FindById(int id);
     //... and so on
}

The usage is pretty simple, and by using IoC you can completely decouple implementation from the rest of your application:

public class MyBusinessClass
{
     private IRepository<SomeDomainObject> _aRepo;
     public MyBusinessClass(IREpository<SomeDomainObject> aRepo)
     {
          _aRepo = aRepo;
     }
     //...and so on
}

This makes writing unit tests a real snap.

Josh E
Take a look at this answer for how to implement this with IoC to manage controllers too - http://stackoverflow.com/questions/2797047/how-do-i-correctly-use-unity-to-pass-a-connectionstring-to-my-repository-classes/2797264#2797264
Russ Cam
I'm a fan of unity - esp. with the improvements they've made in 2.0. I've used it in MVC projects to do exactly that.
Josh E
+1 to this. remember, an IRepository<> doesn't know how it's implemented, and neither does the caller. the caller just wants the model object. if you use an ORM for your IRepository<> implementation, it will take care of all the FK loading for you. if not, you can do it however you want manually, as long as the interface doesn't change and there's no coupling of the interface to implementation.
dave thieben
thanks for elaborating on that dave!
Josh E
+1  A: 

This works in the real world because there's single engine under the hood of the repositories. This can be ORM like NHibernate or your own solution. This engine knows how to relate objects, lock database, cache requests, etc. But the business code shouldn't know these infrastructure details.

What you effectively do when you end up with one giantic repository is exposing this single engine to the real world instead. Thus you mess your domain code with engine-specific details that are otherwise hidden behind repository interfaces.

S#arp Architecture is a very good illustration of how repositories that Josh talk about are implemented and work. Also read the NHibernate best practices article that explains much of S#arp background.

And frankly, I can't imagine how your giantic repository works in the real world. Because the very idea looks bad and unmaintainable.

queen3
Thanks for your thoughtful responses. I wish I could mark them all as answers. I guess I had been too hung up on a 1:1 mapping between domain model and db but as you all point out, the two can be different beasts entirely. Looks like I'll need to read up a bit more on ORMs
duffy