Monday, August 31, 2009

I forgot what the M in MVC was for

What brought me to this conclusion was my reading up on a little Rails. I finally took the dive. I’ve been avoiding Rails for a while in favor of just learning Ruby. Steve loaned me “Rails for .Net Developers,” and the research was underway in earnest.

On page 89, the light bulb went off. But not for Rails so much as for how I’m writing my ASP.Net MVC apps. Here’s the bit that got me:

• Models are the classes that represent your business domain and that are responsible for communicating with your data. In Rails, this means the tables in your database.
• Views represent your presentation layer, for example, HTML and JavaScript.
• Controllers are responsible for connecting the models and views and managing the flow of the application. doing_it_wrong

That reads pretty straightforward to me. Hell, I’ve stood in front of groups of devs on more than one occasion and said, “Views are what gets rendered, controllers marry up the views with anything they need from the model, and the model is everything that’s not a view or a controller.”

So, what’s been going wrong? I’ve been putting way too much business logic in my controllers.

The structure of most of the MVC apps I’ve worked with is a typical .Net program structure. We’ve got a core where our business domain lives, and some type of ORM set up to talk to the database. From there, we expose that domain up to the UI through some simple domain services.

The issue comes when I consume those services. I’m using my controller actions to marry all that up and present it to the UI. I think my “excuse” to this point has been that I have two models: My domain model, which I’m accessing through my domain services, and my presentation model, which lives in the Models directory in the ASP.Net MVC structure. My controllers have to care about two places to get their information to provide to the views. At the least I’m violating the pattern and at worst I’m likely repeating myself somewhere and creating a future maintenance issue.

I think I can see where this happened, too. Think back to the Bad Old Days of dealing with Web Forms, and the cubby code behinds that came with it. In order to avoid the 197 line PageLoad method, I would have a lot of little one-off methods scattered about the code behind file. Switch over to MVC, and it seemed perfectly normal to have a similar structure. Alarms should have gone off when I had 5 classes in the Models directory but 18 methods in one controller class.

But wait, there’s more!

While perusing some code on my current project, I came across the [NonAction] attribute decorating a couple of controller actions. Re-reading that…I had NonAction decorating Actions. That has a certain “Jumbo-Shrimp” feel to it, doesn’t it? I suppose the non-action could fall under “managing the flow of the application” from above, but I’m thinking any non-actions might need to be refactored into the model.

Moving forward on my MVC apps – of any flavor, Ruby or .Net – I think I’ll move to a much thinner controller model in favor of loading up the model with business logic. Though I thought I knew the pattern fairly well, I didn’t practice what I preached.

6 comments:

Steve Horn said...

Probably 80% of our extraneous code in our controllers was dealing with mapping data from Model to ViewModel.

At the very least, that translation code should be moved out into it's own class where it can perform it's one mission in life. Otherwise there's always AutoMapper which I never took the time to dig into. Maybe it's time!

Chris Chandler said...

Everyone has their first app at something, looks back, and says, wow I wrote that?

I think in mvc, everyone immediately gets the thin views and brings along the thin model baggage from other systems. Thus cramming everything in the controllers, to realize that if they'd only fattened up the model, the maintenance and running of the app would be easier.

The rails guys even had little videos over at railsenvy.com that were "public service announcements" to this affect. The first few projects with mvc are game changing.

When I was learning ASP.net mvc I had to relearn this lesson myself, even after learning it hard in rails first.

D. Lambert said...

Tim -

Have you ever looked at Rocky Lhotka's CSLA stuff? He's a proponent of a strong M, too. I agree that many new frameworks come out and help us manage all the infrastructure of the project, but leave the model as an exercise for the reader, which is a shame, since the model should arguably be the most durable part of our code.

Good post.

Mike Busch said...

Great post, Tim! Great demonstration on why branching out into other frameworks/languages can actually help improve your coding practices in your day-to-day language/framework.

I know the same thing happened to my Struts programming after I started playing with Rails, models became, BIG M, Models. :)

Steven Harman said...

Timbo,
if you've never looked at CSLA, please promise me you NEVER WILL! Actually, go take a look at it and I'm sure you run away screaming & rubbing your eyes in disbelief.

OK, to be fair I'm sure it providing value for some teams, in some context. However, knowing you, the way you work, and the core of your software design beliefs, I'm quite sure its not your cup of tea.

At any rate, the Skinny Controller mantra is a good one. I'm moving much closer RESTful controllers where I have a controller-per-resource. I then expose the standard RESTful endpoints (index, list, show, edit, new, create, update, delete) as actions on those controllers, as necessary.

Unknown said...

If you want to see the 'M' done right.... Check out 'Grails'

Very lovely.

(Personally I'm a NHibernate fan)