views:

125

answers:

1

I've been reading up on MVC 2 and the recommended patterns, so far I've come to the conclusion (amongst much hair pulling and total confusion) that:

  • Model - Is just a basic data container
  • Repository - Provides data access
  • Service - Provides business logic and acts as an API to the Controller

The Controller talks to the Service, the Service talks to the Repository and Model. So for example, if I wanted to display a blog post page with its comments, I might do:

post = PostService.Get(id);
comments = PostService.GetComments(post);

Or, would I do:

post = PostService.Get(id);
comments = post.Comments;

If so, where is this being set, from the repository? the problem there being its not lazy loaded.. that's not a huge problem but then say I wanted to list 10 posts with the first 2 comments for each, id have to load the posts then loop and load the comments which becomes messy.

All of the example's use "InMemory" repository's for testing and say that including db stuff would be out of scope. But this leaves me with many blanks, so for a start can anyone comment on the above?

A: 
post = PostService.Get(id);
comments = post.Comments;

Traversing the Model like that is achievable, and desirable. You are absolutely right though that it is an implementation ideal that comes at a performance price, especially when dealing with collections (and you won't have any hair left when it comes to hierarchic data structures).

You'll want to configure NH mappings to do batched lazy loading. (fetch=subselect batch-size=#), otherwise eager loading will pull back too much data, and lazy loading will results in an N+1 selects problem (1 query to fetch the posts, + N queries to fetch comments where N is the number of posts - your loop).

If your requirement is really to show 2 comments for each post, a batchsize of 2 will do, but as you'll no doubt have guessed, as soon as your app tries to access the 3rd comment, NH will perform another select to fill the comments collection with 2 more, so you might want a bigger batch size from the outset. Plan for a perf tuning phase when you know your use cases. This may be very difficult if you are developing a general purpose data access API. (Also, you will want to add an order-by="SOME_COLUMN_NAME" on your comments collection mapping to control how to get the 'first' comments). It's easy to underestimate the importance of the NH mappings settings; ORM solves many dev problems, but adds a whole world of new ones.

'Domain Driven Design' by Eric Evans defines the repository pattern & services. They are not always appropriate. I've stopped using them for all but the very complex projects, and rarely on MVC builds. The benefits of the repository pattern & services are separation, isolation, testability and flexibility of your architectural components. In real-world terms - consider your 'usings' namespaces. If you would rather avoid having 'using nhibernate' in your controllers, then hide it away in a repository and just reference the repo assembly.

This ties in with testability - you can unit test your repo in isolation from the controllers. If you are now offended by having repo references in your controllers then employ a service layer. It's all about loose coupling.

Benefits of a service layer include complete hiding of the data access mechanics, exposing the service methods remotely over other transport options (web services, for instance), and veiling generic repository methods with API friendly names. For example, post = MyAwesomeAPI.PostService.Get(id); might simply be a wrapper to a generic - get any type by id - Repository.Get(id); This API wrapping is massively useful when developing a set of services for 3rd parties to consume, or just other devs on your team. Provided your method signatures stay the same, you can change the underlying implementation at any time - your switch from NH to plain SQL for example would not break existing apps that consume that API.

For maximum flexibility you would not even link your services assembly to your repo implementation assembly at all. Rather you would use a dependency injection tool like Structure Map to wire everything up at runtime. This allows you to switch repo implementations by configuration alone without recompiling/linking. You could even have multiple data access techniques. The consumer of the API would not know, nor should it care.

If you don't need any of those things, put 'using nhibernate' in your controllers and be done. The risk is that you have tightly tied your MVC app to NH and everyone needs to know everything to do make the smallest change to your app. That decision will likely be made by your project constraints (time/money/people/calendar). If you do need all those things, check out Sharp architecture, or assemble your own stack. MVC is so much more VC than M.

mhanney