6

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.

Jerad Rose
  • 15,235
  • 18
  • 82
  • 153
  • 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 Nov 07 '11 at 05:46
  • EF4 (not 4.1). Should I consider moving to 4.1? How hard is it to migrate? – Jerad Rose Nov 07 '11 at 12:50
  • 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 Nov 07 '11 at 12:54
  • 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 Nov 07 '11 at 12:58
  • See: http://stackoverflow.com/questions/3471455/is-dbcontext-the-same-as-datacontext - seems the API is organized a bit nicer, too. Basically just a refactor. – Merlyn Morgan-Graham Nov 07 '11 at 13:00
  • I read up on a couple of reviews of the sample code you linked last night (though didn't look through the code myself). From the versions of the code I saw, it was obvious to me that the sample app's actual code could not be followed or used to understand the principles of each component. I know all the design patterns they were talking about, and they were using pretty much each one of them incorrectly. It seems they knew some of this too, because they had comments in their code saying some of their code was "knowd anti-pattern" :) – Merlyn Morgan-Graham Nov 08 '11 at 02:57
  • Awesome, thanks for checking that out and following up. I'm doing more digging now, and trying to take a step back from my original design, just to see if I can simplify it. – Jerad Rose Nov 08 '11 at 03:05
  • FYI, the reviews I am talking about are here - http://wekeroad.com/post/7102729511/a-simple-example-thats-incredibly-complex and http://ayende.com/blog/19457/review-microsoft-n-layer-app-sample-part-i - both are on supposedly older versions of the code, but the code they reviewed is pretty terrible so I am not sure how much they can save without scrapping everything but the basic entities. Ayende has an example app design he decided to start drafting up in response, but I think he gives up due to his point that you can't invent business processes out of the blue. – Merlyn Morgan-Graham Nov 08 '11 at 03:35
  • http://ayende.com/blog/tags/macto - looks like he might not have given up on it like I thought, though it isn't complete, and there is no publicly visible code yet (probably intentionally, both to illustrate the process, and because he's decided to make the implementation private). – Merlyn Morgan-Graham Nov 08 '11 at 03:37

4 Answers4

6

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)

Chandermani
  • 42,589
  • 12
  • 85
  • 88
6

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.

Community
  • 1
  • 1
Merlyn Morgan-Graham
  • 58,163
  • 16
  • 128
  • 183
  • Thanks @Merlyn. I'm not using 4.1, so I will consider moving to it. Will this not be too hard with an existing 4.0 implementation? Even if it is, I will will still probably do it if the tradeoff is worth it. My repositories currently inherit from a base class using the generic for the model: `UserRepository : Repository`. So it's just a one-line declaration that is repeated for each model (table). I actually script this out using SQL, and copy/paste into a file. But I don't do anything specific for any of the repositories. I will update my answer with more details. – Jerad Rose Nov 07 '11 at 13:09
  • Well, I was going to try to explain my design, but realized it would be too complicated for the time I have right now (need to go to work). I updated my OP with links to an application I modeled, but looks like I need to reconsider this, as it's getting some negative press now. Awesome. I wish I could just find a good article walking through the basics of setting up a good testable MVC app using EF4(.1). That's what I was looking for originally when I came across that article, but unfortunately, it looks like that sent me down the wrong path. – Jerad Rose Nov 07 '11 at 13:38
1

@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.

Erik Funkenbusch
  • 92,674
  • 28
  • 195
  • 291
0

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.

Jason
  • 15,915
  • 3
  • 48
  • 72
  • In most cases, yes, but I do have more complex cases where my services cross borders. But I'm doing this to make my controllers less complex, to keep some of the more complex logic away from the controllers and into the services. In some cases, I'm even injecting other services. For example, my Discussion service gets the Permission service injected into it so that those services can make sure proper authorization is in place before committing a discussion. But maybe this is not the way to go? – Jerad Rose Nov 07 '11 at 13:14