Heh. I don’t actually know if "fat model" is a term outside the Perl
lists/sites I frequent. I don’t have links right now but if some turn up, I’ll /msg you and update. I do have some demo/discussion for consideration.
The idea is simple: business logic is consumed
by everything that consumes the model (DB code) so it needs to be in
the same place to avoid duplication. It’s an extension of the MVC
debate which makes the Controllers *very* thin, doing as little as
possible and containing no business logic outside of authorization
style stuff, the View being quite dumb and not doing much either, and
the Model being "fat."
In a web app, the Controllers and Views might end up doing things
with data; simplistic example: summing. In the fat Model this would be in
the schema code.
This example, being simplistic, will
only involve information that is available to the DB/schema already. It could involve external
info/models/services. This is where things can get really "fat" if
needed. You’d want the external stuff abstracted well so that you
could mock it or run safe dev versions easily.
Example - checkout_total for a cart full of items
Cart has items, items have a cart, and items have a cost.
Obviously the information to get a sum of the items in the cart is
available and can be put together a couple different ways. But it’s a
multi-step process that is going to be needed over and over, so rather
than putting it into DBIC syntax (which can be hairy or alterantively verbose for this kind
of thing), or raw SQL, or letting an iterator do sums in a view, or
some combination of all of those depending on the context, we’ll put
it in the model.
Since it’s an ORM, we already know what calling it should look
like: $cart->checkout_total. There is no limit to how many of
this kind of helper we can write and what it can do is only limited by
judgement.
Let your mind race ahead to write out checkout_total. What
does it look like?
Here’s the idiomatic implementation with DBIC–
sub checkout_total {
+shift->items->get_column("cost")->sum;
}
Already, I hope, you can see that once you get your chops down, DBIC
makes some easy but verbose things even easier and brief. Spelled
out–
# Inside My::Schema::Cart or an auxillary class only
# loaded into the space on demand.
sub checkout_total {
# My::Schema::Cart object.
my $self = shift;
# items is a has_many relationship.
my $items_rs = $self->items; # ->items_rs to avoid contex ambiguity
+.
# Get a DBIx::Class::ResultSetColumn object for cost.
my $cost_col = $items_rs->get_column("cost");
# Perform SUM.
my $sum = $cost_col->sum;
return $sum;
}
After you get your legs, the first version becomes more
legible than the second. And this is really a baby example. You can
continue this sort of thing, riding the relationships, to achieve
really powerful queries that remain readable as they become more
complex. The business logic can become self-documenting and every
piece of every query can be individually testable which will not be
the case with SQL libraries or hand written DBI stuff.
And now the full, working, demo code–
That example is fun and actually useful but a little weak because it’s based on a Result. Resultset stuff is where it gets really cool. There is a really good walk-through of that here: dbix-masterclass.xul, though no one seems to support XUL at this time so you might just have to read the plain text source of the slides.
The caveat with DBIC is that it’s not easy. The learning curve is pretty steep. I think it amply repays the investment.
|