Sunday, June 28, 2009

Converting CDBI to DBIC (part 3): Why C3 is important

If you want to override any of the methods in CDBI, you have to inherit from CDBI, then override using standard OO methods. With any large application, you end up with the explosion of classes in your hierarchy. More than any other hierarchy, the ORM hierarchy really cries out for roles (or traits). Enough of your tables need to be treated the same in some places, but different in others, that you want to be able to say "for classes A, B, and C, their create() is overrided as so, but A and B have their update() overrided as so, but B and C have their delete() overrided as so". With standard inheritance, it's almost impossible to do that and make it comprehensible for the next developer.

DBIC, on the other hand, allows you to define capabilities using components. By providing a traits-like solution, you can easily extend the behavior of your Rows and ResultSets in ways that can cross class hierarchy lines. In other words, it's sane multiple-inheritance-like behavior for a very common use-case of multiple inheritance. In our CDBI example above, you would have three components - one each for the create, update, and delete overrides. A, B, and C would all use the create override, A and B use the update override, and B and C use the delete override. Given that each one is likely to be independent (not related to each other), the code becomes more self-documenting.

Saturday, June 27, 2009

Converting CDBI to DBIC (part 2): The supposed similarities

(You can catch Part 1 for the context)

The first thing you notice when comparing CDBI and DBIC is that the APIs look really really similar. They both have the same method names for almost everything most people use them for. So, you'd think that it's as easy as swapping out use statements, adding a few lines to the table definitions, and calling it a day.

And, unless your application is so exceedingly simple that you were able to keep to the officially published CDBI API, not a single test will pass. (You do have tests, right?) Every problem arises out of the need to have abused CDBI in order to get work done.

Class::DBI is built upon Ima::DBI, a connection caching and SQL management distribution. Every method provided by CDBI is built using Ima::DBI's set_sql() and transform_sql() methods. These methods, while pretty neat, are very hard to extend because they use string transformation.

DBIx::Class, on the other hand, is built on three major concepts that CDBI doesn't have:
  • everything is componentized using C3 method resolution
  • SQL::Abstract to generate the SQL
  • first-class distinction between ResultSets and Rows
These three differences mean that what should, in theory, be a simple conversion between two modules that expose similar APIs becomes a much more difficult thing to do. Over the next few posts, I'll examine why each of these differences is important and how each one complicates the conversion process.

Friday, June 26, 2009

Converting CDBI to DBIC (part 1): The context

For years, Class::DBI was the gold standard ORM in the Perl community, and for good reason. It was simple to deploy, easy to use, and, for the most part, dwim'ed. Oh, we Perlers love our dwimmery. If it doesn't dwim, then we get pissy.

Now, CDBI wasn't designed like Perl. Perl has always been built to make the hard things easy and the impossible merely hard. CDBI, on the other hand, was designed with the 80/20 rule in mind - make as many of the things most people do every day extremely easy. And, for everything else, there's always an easy way to get to use raw SQL. And this was, for most people, good enough. It easily supported rapid prototyping and small applications and, while slighly annoying on the edges, it worked.

But, as applications are wont to do, some of those applications grew up. They didn't stay small or fade away. The schemas grew from 5 or 10 tables to monstrosities of 250 tables or more, many having dozens of columns. The codebases weighed in at well over hundreds of thousands of lines of code. And, instead of building the code, the focus became maintenance. And, those things that were rare in the past became more common. Not relatively more common, but appeared more often. Instead of 3 or 4 dips into raw SQL, there were 90 or 100. And, wow, was scaling an issue.

Many ORMs were built to try and take its place. Lots of good work was done, but the gold standard has seemed to settle around to DBIx::Class, and for good reason. DBIC maintains that 80/20 design philosophy, but manages to marry it to the Perl philosophy of making hard things easy.

Unlike CDBI, DBIC uses SQL::Abstract for its SQL generation, meaning less dips into raw SQL. Scaling is saner because lessons were learned. Yet, all the really easy things are still really easy. Rapid prototyping is still rapid.

And, this is why we want to convert.

(Follow on in Part 2)