Ayende @ Rahien

Refunds available at head office

Skinny Controller, Fat Model

imageScott Bellware has a post about Responsible SRP, he presents the Ruby's idiom of skinny controller & fat model vs. the .Net one of a service layer. He links to this post from Jamis Buck, which explains the Ruby concepts much better. This is also something that is annoying me about my current application, Rhino Igloo doesn't really provide the same level of separation that I consider essential, and as a result my controllers are doing way more than I wish them to. I probably should have broken at least some of them up to services.

I find myself troubled, because while I agree with all that Scott says in this post, and most certainly with Jamis about the overall direction, I can also see the scaling issues of this approach.

The problem is that when you have even a moderately large application and you keep pushing things to the model, it will go beyond fat, it will be obese. You can see some of the tricks to manage it here (I was surprised that I was able to follow them, actually, very neat), but this is most assuredly something that the RoR crowd has already reached.

Let us take the example of the User model, and assume that I would structure my application this way, I don't see it working. My IAuthorizationService has roughly 25 methods already (I am thinking of breaking it up, that is too much), and trying to put them, plus the IAuthenticationService and the various things that the User already does into the User model would create something that would be hard to follow. It certainly wouldn't be in keeping of SRP.

Another approach is to use mixins in Ruby to separate the functionality into different files and mixed it into the User model. I don't really like it, but that is perhaps a matter of taste.

Again, another matter of probably biased conceptions, but I truly like it much better when I can wire up another implementation rather than start modifying code when I need to make a change to the code. My ideal would be a lot of very small classes, all collaborating in order to get some result. This, to me, gives a lot of flexibly and power when I come to utilize them.

I would be interested in getting response from the Ruby community, what happens when the model get too big? How do you break it up then?

Comments

Scott Bellware
09/30/2007 09:31 PM by
Scott Bellware

what happens when the model get too big? How do you break it up then?

Same old way... sharpen the blade used to slice responsibilities, and slice them down to finer, related modules, be they classes, mixins, helpers, whatever.

The difference from .NET would likely be the conspicuous absence of DI as a default means to compose aggregations.

Ayende Rahien
09/30/2007 09:38 PM by
Ayende Rahien

That is interesting, because the use of IoC bring a lot of dynamics to CLR projects.

How do you handle that in Ruby projects?

Ayende Rahien
09/30/2007 09:39 PM by
Ayende Rahien

For that matter, let us take the example of IAuthorizationService, IAuthenticationSerivce and the User model that I have on the CLR.

How would you structure them in Ruby?

Take into account that there are at least two implementation of the services, for AD and hard coded.

Mats Helander
09/30/2007 10:24 PM by
Mats Helander

"Another approach is to use mixins in Ruby to separate the functionality into different files and mixed it into the User model. I don't really like it, but that is perhaps a matter of taste."

Using mixins is an option (with AOP) in .NET as well. Why don't you like it?

/Mats

Andrew Davey
09/30/2007 11:00 PM by
Andrew Davey

If you are using Boo, then maybe there is syntactic macro to implement an interface by calling a given implementation object.

Something like:

public class MyLargeThing implements IFoo, IBar:

[Proxy(IFoo)] _foo : IFoo = CreateFooImpl()

[Proxy(IBar)] _bar : IBar = CreateBarImpl()

You would then have a fat object that is composed of little objects.

Ayende Rahien
10/01/2007 05:22 AM by
Ayende Rahien

Mats,

For the simple reason that is is a fair amount of work, and not nearly as easy and natural as it should be

Ayende Rahien
10/01/2007 05:27 AM by
Ayende Rahien

Andrew,

Yeah, Boo does opens up some very interesting possibilities in this regard, because the cost of using a mixin is removed using this approach.

Tobin Harris
10/01/2007 11:00 AM by
Tobin Harris

I agree with Scott; I always think that you can just re-apply SRP to solve many of these bloated model problems, making finer cuts to pull responsibilites out.

Jamis post is great and proves the point really well IMHO. If this particular model got fat, then you could extract "formatting" issues from the model and put them in a formatter or helper :) Therefore, I'd take the knife and make this kind of incision:

PersonFormatter.name( a_person )

PersonFormatter.pseudoid( aperson )

Mats Helander
10/01/2007 11:33 AM by
Mats Helander

Tobin,

But wouldn't (or at least couldn't) the PersonFormatter be part of the Service Layer? in the MVC example, wouldn't it be enough that the controller knows of the PersonFormatter - no Model classes would need to have references to it, right?

I just added a post where I describe how I think Obese Domain Models is a widespread enough problem that it should be regarded as an anti-pattern.

http://www.matshelander.com/wordpress/?p=75

/Mats

Tobin Harris
10/02/2007 10:18 AM by
Tobin Harris

@Mats

Yeah, the Model wouldn't hold a reference to the Formatter.

If I'm honest, I don't fully "get" what should and shouldn't go in the service layer :) I guess that formatting logic is often duplicated across several "clients", so the service layer might be as good a place as any.

Either way, I think that formatters should be "clost to hand" - a bit like sticking a toolkit in the boot of your car :)

Will check out that post of yours...

Comments have been closed on this topic.