Monday, June 21, 2010

Models are confusing

And they annoy me. Here's an example.

Imagine I have a model that I made some time ago that has nothing to do with Catalyst. This is an ideal situation - best practice is that your models should always be independent of Catalyst - so let's start from here.

Now, the first thing I find I have to do is to create a little file to refer to my model in Catalyst.
package CatApp::Model::MyModel;
use Moose;
extends Catalyst::Model::Adaptor;
has class => ( default => 'External::MyModel' };
It's in a ::Model:: namespace, and it is-a Catalyst::Model. We often refer to it as a model. But it's not much of a model, since it contains no data. It's a shim; it exists to create the model (and to manage its lifecycle, be it per-process or per-request). Even the Catalyst method $c->model doesn't return a CatApp::Model::MyModel, it returns an External::MyModel. So we have the External::MyModel, our model-object-that-contains-data, and CatApp::Model::MyModel, the model-object-that-catalyst-needs-that-isn't-really-a-model-at-all...

Also, you'll notice, it's a verbose bit of configuration. It ties an external class to a Catalyst name (and a set of constructor arguments, pulled out of the config), but in order to do that I need to create a new file, 5 lines of boilerplate plus any special config I'd like to give it. Eww.

In the not-too-distant future, Bread::Board is coming to Catalyst. It should make it possible to take your data models and pull them into Catalyst, complete with construction and lifecycle, all in a single chunk of configuration. Better still, you'll be able to take that config and use it outside of Catalyst so that you can do sensible model layer integration tests. Can't wait.

In the meantime, remember when you post on the mailing list or the IRC channel that the word 'model' is a bit ambiguous.

2 comments:

  1. The question is why do you need to call $c->model('WhatEver') ? Why not just do WhatEver->do_what_i_want( ... ) ? There are valid reasons to do that, one of them is the need to pass parameters to WhatEver at the creation time from the application config, but I believe that for most of the time this is just cargo-culting. $c->model makes Catalyst a kind of Service Locator (an alternative to the IOC of Bread::Board) - but it was never really thought through.

    ReplyDelete
  2. What I've found myself doing is writing a complex model - something that takes a lower level model, such as a database, and abstracts it - and then using discovery with $c->model in the shim to go find the DB model it should be working on.

    But, indeed, if you use Bread::Board for all the work, $c->model becomes entirely redundant, particularly if you pass (sysadmin-editable) Catalyst configuration, such as your DB connection strings, to it, and then get it to hand that out to the objects that it creates.

    ReplyDelete