[Catalyst] best practices for Catalyst development
Dominique Quatravaux
dom at idealx.com
Fri Aug 12 14:13:10 CEST 2005
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
David Storrs wrote:
> Yes, it may. I'm quite prepared to believe it. I'm also prepared
> to believe that there really is a difference. Can we talk about
> it and come up with reasons for and against, or do we need to pick
> one view and take it as received wisdom?
In hindsight I understand my message could be understood as a cheap
shot, which was not intended to be - Please accept my apologies.
> Could you, or Perrin, point me at his arguments?
Well, see his own answer, AIUI he mostly has some diverging opinion
wrt. the M / C frontier. I concur to his very pragmatic point that not
allowing the business-logic to see the stash is key to its reuseability.
> I've seen a lot of assertions, but so far I don't believe anyone
> in the thread-- including me--has said "I do XYZ because of ABC.
> Here are the pros and cons that I evaluated to make this
> decision."
>
Actually I have some difficulty articulating the pros / cons of my
design choices (especially the cons :-) and Perrin does a much better
job of that (and while being orders of magnitude more helpful and polite).
Let me try nonetheless using a practical example: right now I'm doing
some kind of a permissions management GUI, which allows one to define
Groups, Users, Privileges, attach Privileges to Groups and Groups to
Users. Groups can also inherit their Privilege set from each other
(e.g. "tech director" inherits from "tech project leader" while adding
Privilege READ-WRITE to ALL_ACCOUNTS in the "project management app"
on top of that). This is my first Catalyst app.
I had to solve the following architectural problems (roughly by order
of appearance):
1) Persistence
* what I did = I drafted a SQL schema and painted some Class::DBI
over it (MODEL only).
* Pro = it was quick for simple stuff.
* Con 1 = it was not straightforward enough for me for has-many
relationships (I ended up coding a pair of overengineered
extension packages to Class::DBI, wasting a fair amount of time).
* Con 2 = my code now smells SQL and I don't like that. I was
expecting this difficulty (also known as the object-relational
impedance mismatch). E.g. a Privilege "has a" Role because in
the SQL schema the Privilege table has a "Role" column, even
though it doesn't fit my abstraction (it's the other way around
in my head, e.g. I want to do Privilege algebra calculations
without taking Roles into account in any way). With yet more
overengineering, I have a plan to get past this, though.
* Conclusion = SQL bends my Model, I get over it.
2) Basic CRUD for the 3 main classes (Privileges, Groups, Users)
* what I did = I pulled off some conventional Catalyst
(CONTROLLER) and Template::Toolkit (VIEW). I plundered Maypole's
template set.
* Pro = it Just Works and it's a pleasure to code. The ->forward()
primitive is groovy, it allows to do multi-page wizards in a
straightforward way. The Maypole templates and CSS look great.
The URL -> action mapping done as function attributes is
terribly cute, Perlish, straight in the party line. I love it!
* Con = Catalyst being a framework of multiple parts, ironing out
some bugs required me to deep dive into the source code of a lot
of CPAN modules, which was a very time consuming task. I chalk
this up as an investment (this was also my first venture with
Class::DBI). I don't have any issue with the resulting software
architecture, however.
* Conclusion = for *simple* Web apps, the Model is indeed so thin
that you can see through it (no "business" in Model yet). But it
won't last! (See below).
3) Constraint checking
* Problem = we have to prevent loops of sub-Roles from appearing
and the like.
* What I did = I implemented that within the MODEL
(MyApp::M::Privilege), using Class::DBI constraints.
* Pro = I'm firmly convinced that the MODEL is where this stuff
belongs. A cron job has no more right to create a loop than the
GUI has, plus this is a whole-app coherence issue that SQL
cannot (easily) capture.
* Con = if for some reason another app (not in Perl) is later
given direct read-write access to the SQL database, it will be
able to mess up with the model constraints. Not that I want to
do that in any case ! See below.
* Why not in SQL = I hate expressing constraints in SQL (the
standard thing for proponents of thin-model architectures). SQL
constraints are cumbersome, non portable between DB's and quite
limited in power, in a word they solve the wrong problem (making
the SQL-level access an API graft point, a big no-no if you want
any kind of upgradeability in your app !) in the wrong way (by
designing yet another programming language - I have Perl, thank
you very much).
* Conclusion = now we are beginning to see the autonomous
usefulness of the Model, because our app has evolved beyond a
simple SQL table editor.
4) GraphViz
* Problem = I want to display the graph of Roles that inherit from
each other.
* What I did = I coded MyApp::V::Role (VIEW, and not
Template::Toolkit) and I called that class from
MyApp::C::Role::graph : Path("graph.png")
* Pro = I can draw graphs from the command line, using the same
Model and View.
* Con = N/A
* Conclusion =My code separation is the right thing, it just
showed reuseability.
5) Leveling the problem space : there is a world beyond CRUD
* Problem = I want the end user to be able to set Roles *and*
Privileges on the same screen (pointy-haired boss wants to
*remove* Privileges even though his victim theoretically belongs
to the adequate Role, grin)
* What I did = I implemented a slew of dedicated methods in
MyApp::M::User (MODEL) that deal with Privileges and Roles
(->has_privilege(), ->add_privilege(), ->subtract_privilege()
and so on) in terms of the already existing persistent
accessors. I also implemented Privilege algebra operations in
MyApp::M::Privilege (->compare(), ->simplify_list()). SQL bent
my Model some more. I then implemented non-standard CONTROLLER
and VIEW screens for His Pointy-Hairedness, using these as
primitives.
* Pro = my model classes are now meaty, ready for unforeseen
future feature requests, still their exported interface really
captures useful stuff that I want to do with them (I know,
because my Controller calls them).
* Con = I have to think and code a lot, and in steps (first design
Model, then test it, then code it, then code Controllers, then
test them with bogus debugging Views, then code the real Views
and finally do acceptance testing). Catalyst is no longer
shouldering the CRUD for me.
* Conclusion = The gap between problem space and solution space
becomes thicker, and my workload goes up. Nothing unexpected.
6) Looking at it all in hindsight
* What I have = a Model that mixes persistence and business logic,
and blends them together without separation of sub-concerns (the
code that solves the "has a Role" problem lives in the same
package, albeit under a different POD section, that the one that
does Privilege algebra).
* Pro = this is very easy to unit-test
* Con = I fear that this lack of separation between concerns in
Model will come and haunt me later
* Conclusion = I'm not sure.
Global conclusion
* I'm positive I have got the correct boundaries set between M, V,
and C because I'm already reaping the benefits (unit tests,
reuseability). Perhaps M could use more internal refactoring,
I'm not sure yet.
* the typical web app is not an adequate showcase for MVC: CRUD is
just too simple. Only when your app has grown in complexity, can
you be sure that your design is good. You could get away with
inappropriate MVC if doing shopping carts (no offence intended
to anybody - the challenging stuff is elsewhere, eg. scalability).
* It seems that each time I upgrade my app, SQL gets in the way. I
know it, I fear it, but there is little I can do about it. I
could bend the SQL to fit my Perl instead of the other way round
(Tangram) but then my database schema would lose legibility and
my boss would not like it. (Open-source adequately-hyped
SQL-less OODBMS for Perl anyone?)
> Also, this discussion is aimed at **best practices**. We all know
> that in the real world of short deadlines, we take shortcuts. This
> is about how you would do it if you were doing it The Right Way(tm)
>
Actually I have some R&D time alotted for this so I'm doing the best I
can for quality. I'm trying to acquire adequate tools and reflexes for
the time I'll be using Catalyst in crash-landing projects.
- --
Dominique QUATRAVAUX Ingénieur senior
01 44 42 00 08 IDEALX
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.2.5 (GNU/Linux)
Comment: Using GnuPG with Thunderbird - http://enigmail.mozdev.org
iD8DBQFC/JJWMJAKAU3mjcsRAkrEAJ94kKSfho/TuukGVCATuHUggCZ/OgCbBhEB
7HWAiuGaimSDgSiKL2ZqULQ=
=9wiK
-----END PGP SIGNATURE-----
More information about the Catalyst
mailing list