[Catalyst] Making Controllers thin and Models thick

John Napiorkowski jjn1056 at yahoo.com
Wed Jul 18 15:49:50 GMT 2007


--- Bill Moseley <moseley at hank.org> wrote:

> On Wed, Jul 18, 2007 at 09:39:25AM +0000, Zbigniew
> Lukasiak wrote:
> > Hi there,
> > 
> > I am too working on a thick model component.  It's
> a ResultSet base
> > class with functions bearing (provisional?) names:
> build_create and
> > build_update.  They do params validation and
> update the database in a
> > transaction with data for both the individual
> record and related
> > stuff.
> 
> Sounds like both you and Ken are pushing the
> validation into the
> model -- or rather the ORM layer.  The model needs
> to do some
> validation, of course, and I like to push that into
> the RDBMS where
> possible.
> 
> I do wonder if that's not coupling the ORM and the
> validation too
> closely.  Not that it happens very often, but if you
> decided to change
> ORMs you would would still want the same validation
> of input data.
> 
> I tend to have a separate layer between the
> controller and the model
> for validation.
> 
> > In short the CRUD controller actions using it can
> be as simple as:
> > 
> > sub update : Local {
> >    my ( $self, $c, $id ) = @_;
> >    if ( $c->request->method eq 'POST' ){
> >        my $update_result = $c->model( 'DB::Table'
> )->build_update(
> > $id, $c->request->params() );
> >        if ( $update_result->{status} eq 'OK' ){
> >            $c->res->redirect( $c->uri_for(
> "table/view", $id") );
> >        }else{
> >            $c->stash( update_result =>
> $update_result );
> >       }
> >    }else{
> >        $c->stash( item => $c->model( 'DB::Table'
> )->find( $id ) );
> >    }
> > }
> 
> 
> I suspect you write that enough times and you will
> notice the common
> code.
> 
> My Create/Update controllers tend to look like this:
> 
>     sub edit : Local {
>         my ( $self, $c, $id ) = @_;
> 
>         $c->post_redirect( 'list', 'Foo was updated'
> )
>             if $c->update_from_from( $id );
> 
>     }
> 
> Because update_from_form knows the action path and
> can therefore find
> the associated form.  It can also determine what the
> concept of a
> posted form is in a single place.  And the
> associated form knows what
> model class to update or create a row in.  And the
> model class knows
> how to validate ids.  And the form knows how to look
> at the model
> class and determine lookup values for one-to-many
> and many-to-many
> relationships.
> 
> I might validate $id a bit more to make sure the
> current user can
> access it, but that can also happen in a chain or
> auto method.  Or
> that might be considered part of the validation and
> the form module
> can validate that the user can access $id.  But, I
> kind of consider
> that more of a controller responsibility since an
> invalid $id in the
> path for a given user is an invalid path (and thus
> maybe a 404).
> 
> -- 
> Bill Moseley
> moseley at hank.org
> 

I've come to the reluctant conclusion that for
anything other than trivial applications you will need
to validate in a couple of places.  Obviously the
database should be properly designed to enforce
integrity rules.  If you are using a database that
let's you create custom types, like Postgresql, you
can take it a bit further and actually create self
validating types for your columns.  I do this for very
common things like email addresses.  Postgresql is
nice for this since you can create custom types and
domains using Perl as your procedural language.

I end up mirroring a lot of this in DBIC using
DBIC::Validate since I'd rather catch syntactical
errors in my code instead of throwing a database
error.  For me that's the last line of defense.

Validation in the business logic is a different beast
since here you are enforcing not just types value but
actual business rules.  Although in theory you can do
all of this in the database or at the ORM level it's
usually best not to since your business logic is
actually trying to capture the full domain of activity
for your system, and this is hard to do with tables
and triggers.  Not impossible, just can be messy and
not too flexible.

I tend to think of this as 'spell checking versus
grammar checking'.  Your domain code is the grammar
for a particular business activity.  At least I think
of it that way.

At the User Interface level you also have checking
that has it's own needs.  For example you might have a
business process to create a new account for a user. 
At the UI level you ask for a new password and to
repeat the password in another field to verify that
you typed it correctly.  For me that is validation for
a user interface model and doesn't belong in the
domain logic, ORM or in the controller.  This is where
reading the code for the Reaction project has really
helped me to think about this.  

If this sounds like validation is scattered all over
your code, it doesn't have to be.  Because validation
tends to be a lot of configuration files stuff that
you can centralize if you want.  Data::Formvalidator
can cover a lot of this, since you can use it in DBIC
and has decent integration with Template Toolkit if
you are using that for your presentation layer.

--john



       
____________________________________________________________________________________
Need a vacation? Get great deals
to amazing places on Yahoo! Travel.
http://travel.yahoo.com/



More information about the Catalyst mailing list