6
votes

I'm building an MVC3 app, trying to use IoC and constructor injection. My database has (so far) about 50 tables. I am using EF4 (w/ POCO T4 template) for my DAC code. I am using the repository pattern, and each table has its own repository. My service classes in my service layer are injected w/ these repositories.

Problem: My service classes are growing in the number of repositories they need. In some cases, I am approaching 10 repositories, and it's starting to smell.

Is there a common approach for designing repositories and service classes such that the services don't require so many repositories?

Here are my thoughts, I'm just not sure which one is right:

1) This is a sign I should consider combining/grouping my repositories into related sections of tables, reducing the number or dependent repositories per service class. The problem with this approach, though, is that it will bloat and complicate my repositories, and will keep me from being able to use a common interface for all repositories (standard methods for data retrieval/update).

2) This is a sign I should consider breaking my services into groups based on my repositories (tables). Problem with this is that some of my service methods share common implementation, and breaking these across classes may complicate my dependencies.

3) This is a sign that I don't know what I'm doing, and have something fundamentally wrong that I'm not even able to see.

UPDATE: For an idea of how I'm implementing EF4 and repositories, check out this sample app on codeplex (I used version 1). However, looking at some of the comments there (and here), looks like I need to do a bit more reading to make sure this is the route I want to take -- sounds like it may not be.

4
EF4, or 4.1? The repository pattern and unit of work are built into the context in the DbContext template in 4.1 (well, maybe with a one-liner tweak to the template...)Merlyn Morgan-Graham
EF4 (not 4.1). Should I consider moving to 4.1? How hard is it to migrate?Jerad Rose
You'd need to throw out your old template/model code generation, and generate a new template, but the class names will be the same. That parts just a few clicks. Some of the base methods on your context (and maybe on the data sets) will break, and it depends on how many of those methods you use on how big the impact will be. I believe those will mostly be a textual change, rather than having to swap out much logic.Merlyn Morgan-Graham
The main reason you'd swap is that the 4.0 classes will eventually go away (might be a while though - I don't think they're marked as deprecated at this point). Besides that, the 4.1 classes have a somewhat better interface and they are more conducive to inserting mock objects. You can return IDbSet as your repositories, which is more easily mockable with Linq to Objects data than DataSet was.Merlyn Morgan-Graham
See: stackoverflow.com/questions/3471455/… - seems the API is organized a bit nicer, too. Basically just a refactor.Merlyn Morgan-Graham

4 Answers

6
votes

If you look at DDD Aggregate Root pattern and try to see you data in this perspective you would realize that many of the table do not have a independent existence at all. Their data is only valid in context of their parent. Most of the operations on them require you to get the parent as well. If you can group such tables and find the parent entity\repository all other child repository can be removed. The complexity of associating the parent child which till now you would be doing in your business layer (assuming you are retrieving parent and child using independent repo) not would be shifted to the DAL

Refactoring the Service interface is also a viable option, and any common functionality can be moved into a base class and\or can be itself defined as a service which is consumed by all your existing services (Is A vs Has A)

6
votes

Chandermani is right that some of your tables might not be core domain classes. This means you would never search for that data except in terms of a single type of parent entity. In those cases you can reference them as "complex types" rather than full-blown entities, and EF will still take care of you.

I am using the repository pattern, and each table has its own repository

I hope you're not writing these yourself from scratch.

The EF 4.1 already implements the Repository Pattern (DbSet), and the Unit of Work pattern (DbContext). The older versions do too, though the DbContext template can easily be tweaked to provide a clean mockable implementation by changing those properties to an IDbSet.

I've seen several tutorial articles where people still write their own, though. It is strange to me, because they usually don't provide a justification, other than the fact that they are "implementing the Repository Pattern".

Writing wrappers for these repositories for access methods like FindById make it slightly easier to access, but as you've seen is a big amount of effort potentially little payback. Personally, unless I find that there is interesting domain logic or complex queries to be encapsulated, I don't even bother and just use Linq directly against the IDbSet.

My service classes in my service layer are injected w/ these repositories.

Even if you choose to use custom query wrappers, you might choose to simply inject the DbContext, and let the service code instantiate the wrappers it needs. You'd still be able to mock your data access layer, you just wouldn't be able to mock up the wrapper code. I'd still recommend you inject less generic ones though, because complex implementation is exactly the type of thing you'd like to be able to factor out in maintenance, or replace with mocks.

1
votes

@Chandermani has a good point about aggregate roots. Repositories should not, necessary have a 1:1 mapping to tables.

Getting large numbers of dependencies injected in is a good sign your services are doing too much. Follow the Single Responsibility Principle, and refactor them into more manageable pieces.

0
votes

are your services writing to all of the repositories? i find that my services line up pretty closely with repositories, that they provide the business logic around the CRUD operations that the repository expose.