DBICx::Modeler::Generatorでスキーマクラス群とモデルクラス群を一発生成しようB!

ご挨拶

はじめまして、gardejoこと守屋と申します。金融ユー子で働いています。YAPC::Asia 2009特別研修で、(16日目を執筆された)dankogaiさん研修の後に、(15日目を執筆された)lestrratさんなどの特別補講を受ける機会に恵まれたのですが、「業務でCOBOLを使っている人?」という質問にただ独り挙手して、たいそう恥ずかしい思いをしました。

そんな勤め先では定例作業撲滅のためなどにPerlをゲリラ的に活用していますが、現場レベルでの対症療法であるに過ぎません。私にとってのPerlとは、エスペラント日本語翻訳システムの開発など、趣味の言語として楽しむ対象です。従って、このhacker trackの執筆陣として居並ぶハッカーの方々には多分に見劣りしますが、どうかご容赦ください。

本日はMooseDBICx::Classが大好きな人にお勧めしたいアプリケーション設計方法論を踏まえて、DBIx::Class::Schema::LoaderDBICx::ModelerのヘルパーモジュールであるDBICx::Modeler::Generatorをご紹介します。

目次

  1. モデルをWAFともスキーマとも分離する設計
  2. クラスの山をどう作るか
  3. 作業の実際
  4. 実用上の留意点
  5. 中身の話
  6. まとめ ~ Perlはエンタープライズアプリケーションに向いています!

1. モデルをWAFともスキーマとも分離する設計

1.1. エンタープライズアプリケーションで一番大事なビジネスロジック

いわゆるエンタープライズアプリケーションの開発に際しては、大量のデータにまみれることになります。ここでの大量とは、テーブルの行方向も指しますが、むしろテーブルの列方向や、テーブル自体の数をこそ指すものと思ってください。

しかし扱うべきデータの量に圧倒されていてはいけません。エンタープライズアプリケーションで一番大事なものは何でしょうか。それはlestrratさんの『モダンPerl入門』でも触れられている通り、ビジネスロジックなのです。

1.2. ビジネスロジックの書き方

PoEAA("Patterns of Enterprise Application Architecture", 邦訳『エンタープライズアプリケーションアーキテクチャパターン』)では、ビジネスロジック(PoEAAでは「ドメインロジック」という用語が使われています)を実装するパターンとして、

  • Transaction Scriptパターン
  • Domain Modelパターン
  • Table Moduleパターン

が挙げられています。このうち、私は特にDomain Modelパターンを強くお勧めしたいところです。これらの違いを簡潔に説明し、かつ、Domain Modelパターンのすばらしさをまとめている資料として、ouobpoさんの「ドメインロジックの実装方法とドメイン駆動設計」というスライドが参考になります。

要はせっかくオブジェクト指向で生活しているんだから、データと振る舞いを分けるTransaction Scriptパターンではなく、データと振る舞いをカプセル化した「賢いデータ」を使うDomain Modelパターンを使うと楽しいよ、ということです。モデリングの敷居は多少高いけれども、業務を可視化して綺麗に書ける利点はとても大きいです。

ouobpoさんはまた日本ではTransaction Scriptパターンが優勢とも述べています。しかし動的言語であるPerlであれば、Javaのように「静的言語を動的に扱う道具仕立て」自体が存在し得ないので、「いいところどり」を出来る余地があるのではないでしょうか。

実際にPerlでDomain Modelパターンに基づいて書いてみると、拍子が抜けるくらいに素直に実装出来る物です。食わず嫌いをやめてみて、一度試してみる価値は十分にあると思います。

1.3. ビジネスロジックを他の層と分離する

さてそんなビジネスロジックは、ouobpoさんのスライドの中にあるように、

  • プレゼンテーション層
  • ドメイン層
  • インテグレーション層

のうち、ドメイン層に位置します。

『モダンPerl入門』では、ビジネスロジックはCatalystなどのWAF(ウェブアプリケーションフレームワーク)とは分離されていて然るべきものだ、と言及されています。これはプレゼンテーション層とドメイン層を分離するという意味です。

そしてインテグレーション層にはDBIx::Classなどが当てはまりますが、これもドメイン層と分離されていて然るべきです。

lestrratさんの「MVCのモデルはDBじゃなくてもいいんだよ」の記事にあるように、モデルはDBとのやりとりとは超然としているべきであって、インテグレーション層を取り扱うクラスを別途設けるのが手堅い方法論です。lestrratさんの記事では、Model::DBICを別個設けるという方法論が例示されています。

1.4. Perlに於けるDomain Modelパターン

Perlのウェブアプリケーション作成の定番としてCatalystDBIx::Classを利用する場合で、Domain Modelパターンに基づいた設計をするとどうなるでしょうか。これについては、(7日目を執筆された)dannさんによる「CatalystからModelを切り離せ - MVCのMのあるべき姿 -」のスライドが一つの答えになります。

これは「Catalystを使っていて困ること」から10を超える記事を経て「CatalystでのMVCの私的まとめ」へ至って導き出された考え方で、順を追うととてもよく理解が出来ます。

スライド24枚目にあるように、ドメイン層であるモデルクラスがSchema::*などを参照するということで、実際にはPOPO Modelのさらに奥にインテグレーション層であるスキーマクラスがあることになります。

なお、モデルの後ろにスキーマが必ず控えているとも限らないことも留意しておく必要があります。例えばValue Objectパターンに基づくモデルクラスを追加で作ることもあるでしょう。

1.5. Mooseベースのクラスでモデルクラスを書く

さてそのモデルクラスをどう書きましょうか。上述のdannさんの例では、POPO(Plain old Perl object)と書かれていますが、これはCatalyst::Modelベースではないという意味だと理解しています。つまり、そこでは古式ゆかしいClass::Accessor::Fastを使うこともあるでしょう。そして、折角ならばみんな大好きなMooseを是非使いたいところです。

そして「Schema::*などを参照する」という作業をやってくれるのが、DBICx::Modelerというモジュールです。これはDBIx::ClassスキーマとMooseクラスを仲立ちする薄いレイヤーで、橋渡しとしては申し分のない出来となっています。これにより、モデルクラスからスキーマクラスを透過的に取り扱えるようになります。

1.6. DBICx::Modeler以外の解

MooseDBIx::Classとを繋ぐ方法としては、他にスキーマの記述もMoose記法で書いてしまおうという野心的なMooseX::DBICというモジュールがあります。

また、DBIx::ClassというORMにこだわらなければ、KiokuDBFey::ORMなどを活用する手法もあるでしょう。

これらはいずれも魅力的な方法ですが、モデリング結果をコードに落とし込むことを自動化したいという要求に焦点を当てたいので、本稿ではこれ以上は触れないこととします。

2. クラスの山をどう作るか

上述の通りエンタープライズアプリケーションではデータが膨大になり、込み入った業務を目指すと、途端にテーブルが数十個、場合によっては百個超に膨れあがってしまいます。

モデルクラスという層を新たに設けることにしたとして、スキーマクラスだけでも2桁・3桁になり得るのに、さらにそれに前後する量のモデルクラスを手作りするのは、気の遠くなる話です。

2.1. DBIx::Class::Schema::Loaderによるスキーマクラス群の一発生成

ただし、スキーマクラス群の生成には、定石と言える方法があります。DBIx::Class::Schema::Loaderを使って、今そこにあるDBを解析して、一発でコードを生成するというものです。

そうであれば、DBICx::Modelerを使ったモデルクラス群の生成も何とか自動化し、単純作業は撲滅したいところです。

そもそも、開発者にとって一番信用ならない人間は誰でしょうか。私は、それは自分だと思います。こと他人であれば事細かく確認したであろう内容であっても、自分に対しては「奴(=自分)はよしなにやってくれるだろう」と見つめてしまうからです。

私は上述のスキーマクラスを作る方法でさえ面倒……もとい、自分の犯しがちな間違いを恐れています。ライブラリ探索パスをuse libで切り替えて2回スクリプトを流すという工程では、DBを直してはコードを生成するという作業を何度か繰り返しているうちに、use libのコメントアウトをトグルし忘れて流してしまう自信があります。

2.2. DBICx::Modeler::Generatorによる(スキーマクラス群と)モデルクラス群の一発生成

ということで、そんな自堕落な自分のために、DBICx::Modeler::Generatorというヘルパーモジュールを作ってみました。いやはや、ようやく本題に入れました。

これは、DBIx::Class::Schema::Loaderの一連の作業をラップした上で、さらにDBICx::Modelerのモデルクラス群をも一発生成するという代物です。

3. 作業の実際

それでは、DBICx::Modeler::Generatorを使った作業を、順を追って見ていきましょう。

ここでは、DBIx::ClassのPODやDBICx::Modelerのテストでも挙げられている、

  • artist
  • cd
  • track

というテーブルと、それに関連するスキーマクラス群およびモデルクラス群として、以下のようにクラス群を生成することを目的とします。

path/
    to/
        approot/
            lib/
                MyApp/
                    Model/
                        Artist.pm
                        Artist/
                            Rock.pm
                        Cd.pm
                        Track.pm
                    Schema.pm
                    Schema/
                        Artist.pm
                        Cd.pm
                        Track.pm

なお、DBICx-Modeler-Generatorディストリビューションには、本稿で触れた内容を全てexamples/ディレクトリ以下に収めています。

また、本稿はPOD日本語版)を再構成したものです。PODにはこれ以外のちょっとしたtipsも載せていますので、適宜そちらも参照してください。

3.1. MySQL Workbenchにより、テーブルを設計

ここでの例として、テーブル設計ツールとしてMySQL Workbenchを利用します。

GUIツールであるMySQL Workbench自体は直感的な操作が可能ですので、特に迷うことはないと思います。唯一の落とし穴は、関係(relationship)をGUIで定義する際に、関数従属される方からする方へ順にテーブルをクリックしなければならないことです。つまりArtist has many CDという関係を定義する場合、1:Nのボタンを押下してカーソルを1:N選択モードにした後で、cdテーブル→artistテーブルの順に押下する必要があります。

この作業の成果物は、ディストリビューションにも同梱しているexamples/doc/DBDSC_schemata.mwbを参考にしてください。

3.2. ER図の生成

MySQL WorkbenchではER図を出力することも出来ます。スキーマクラス群やモデルクラス群を生成するためには必須ではありませんが、文書化という観点では必須と言えるでしょう。

[File] - [Export] - [Export as PNG...]メニューを辿ってPNGで出力した例も、examples/doc/DBDERII_Including_Information.pngとして同梱しています。SVGやPDFでも出力出来るので、用途に合わせて出力しましょう。

3.3. DBへの反映、またはDDLスクリプトの生成

[File] - [Export] - [Forward Engineer SQL CREATE Script...]メニューを辿り、DDL(要はCREATE文)のスクリプトを生成します。DDLスクリプトの例はexamples/src/myapp.sqlです。

なお、一発で完全なスキーマを作れることはまずないでしょうから、既存のDBにあるテーブルをDROPするよう、生成時オプションで記述しておくのが無難です。

後述の作業で、このスクリプトの内容をDBに反映させます。勿論、DDLスクリプトからDBへ反映する際には、RDBMSのCLIを使う方法もあります。

MySQLならば

mysql < foobar.sql

SQLiteならば

sqlite3 < foobar.sql

などです。実際のところ、DBICx::Modeler::Generatorは内部で上記をやっているに過ぎません。

MySQL Workbenchではさらに、[Database] - [Forward Engineer...]メニューを辿り、設計したスキーマを直接DBに反映することも可能です。

3.4. 自動生成されない内容をスキーマクラスに記述

先にご紹介したみなさんのDBIx::Class::Schema::Loaderの利用例にあるように、自動生成されない内容を同名クラスで予め記述しておき、生成先とは別のパスに保存しておきます。この例では、path/to/approot/lib/Schema/以下ではなく、path/to/approot/src/lib/Schema/以下に保存することにしましょう。

path/
    to/
        approot/
            lib/
            src/
                lib/
                    MyApp/
                        Schema.pm
                        Schema/
                            Artist.pm
                            Cd.pm
                            Track.pm

或るクラスについて追加するコードが何もない場合は、そのクラスをpath/to/approot/src/lib/MyApp/Schema/以下に用意する必要はありません。

追加するコードとしてよくある内容としては、

  • インフレーション(inflations: データベースから取り出す際にオブジェクト化する処理など)
  • デフレーション(deflations: データベースへ格納する際にオブジェクトを文字列化する処理など)
  • 追加の関係(relationships)

などが挙げられます。

注意しなければならない点として、ここで記述したクラスはそれ単独でPerlクラスになっていなければならいない、ということが挙げられます。例えばパッケージは真を返さなければなりません。また、DBIx::Class::Schema::Loaderに拾ってもらうため、packageもきちんと書かなければなりません。具体的には、同梱したexamples/src/lib/MyApp/Schema/Artist.pmのように書く必要があります。

3.5. 自動生成されない内容をモデルクラスに記述

スキーマと同様、モデルについても、自動生成されない内容を記述しておき、生成先とは別のパスに保存しておきます。この例では、path/to/approot/lib/Model/以下ではなく、path/to/approot/src/lib/Model/以下に保存することにしましょう。

path/
    to/
        approot/
            lib/
            src/
                lib/
                    MyApp/
                        Model/
                            Artist.pm
                            Artist/
                                Rock.pm
                            Cd.pm
                            Track.pm

ここでもスキーマと同様に、或るクラスについて追加するコードが何もない場合は、そのクラスをpath/to/approot/src/lib/MyApp/Model/以下に用意する必要はありません。モデルをスキーマの数だけ用意する必要は必ずしもありませんし、或いは逆に、MyApp::Model::Artist::Rockのようにスキーマにないモデルを用意することもあり得ます。

ここではスキーマと違って、Text::MicroTemplate::Extendedのテンプレートとしてコードを記述します。具体的には、Baseテンプレートを継承し、codeブロックに追加したいコードをそのまま記述します。具体的には、同梱したexamples/src/lib/MyApp/Model/Cd.pmのように書きます。

「自動生成されない内容」として追加するコードとしては、「スキーマクラスを透過的に取り扱う」以外にモデルクラスでやりたいこと全てです。そもそも「自動生成される内容」とは、use DBICx::Modeler::ModelとPODの雛形程度に過ぎません。モデルには沢山のビジネスロジックが記述されるでしょう。

後はlestrratさんが『モダンPerl入門』で提唱するように、MyApp::APIなどのサービスクラスを用意し、モデルを跨いだロジックや、モデルを用意しない場合のスキーマクラス用のロジックを記述していくことになります。

あくまで例なので、ディストリビューションに同梱したサンプルにはビジネスロジックを何も記述していません。MyApp::Model::Cdでは、priceアトリビュートを追加したり、play()メソッドを追加している程度です。前者については、この例ではDBIx::Class::VirtualColumnsを利用する方が良いでしょうけれども、Mooseアトリビュートであれば

  • ロールでのアトリビュートの定義
  • lazy_buildによる相互依存の合理的表現
  • 各種メソッドモディファイヤーでの柔軟な処理

等々の魔法が使えるので、用途に応じて使い分けるのも一つの手かも知れません。

3.6. コマンド一発でDB反映・スキーマ生成・モデル生成

さあ、これで必要な材料は揃いました。後述するようにDIを利用したり、DBICx::Modeler::Generator::CLIで調子に乗ってMooseX::Getoptを利用しているので、面倒な設定は全て外に出しておきましょう。

実行用のスクリプトではexamples/src/sbin/maintain_models.plのように、

use DBICx::Modeler::Generator::CLI;
my $generator = DBICx::Modeler::Generator::CLI->new_with_options->generator;
$generator->deploy_database;    # DDLスクリプトをDBに反映
$generator->update_schemata;    # DBIx::Class::Schema::Loaderでスキーマクラス群を生成
$generator->update_models;      # Text::MicroTemplate::Extendedでモデルクラス群を生成

と書きます(上記の3.3.で、DDLスクリプトを介さずにスキーマを直接DBに反映済みであれば、deploy_database()メソッドを呼ばずにコメントアウトしてください)。

必要なオプションを付けて、上記のスクリプトを呼びます。

maintain_models.pl -a MyApp -r path/to/root -d MySQL
  • -a, --applicationでは、アプリケーション名MyAppMy::Appなどを指定します。
  • -r, --rootでは、クラス群を生成する先である、アプリケーションルートパスpath/to/approotなどを指定します。
  • -d, --driverでは、お使いのRDBMSに適合したドライバーを指定します。MySQLならばMySQL、SQLiteならばSQLiteです。

DB接続用のユーザー・パスワードや、DBサーバーのホスト・ポートやら、諸々の指定についても、必要に応じて指定してください。

MooseX::SimpleConfigも利用しているので、設定ファイルに上記を記述しておいて、--configfileで設定ファイルのパスを渡すだけでも良いです。

設定ファイルの例はexamples/src/myapp.ymlにあります。

3.7. これで完成です!

以上で、モデルクラス絡みのコード記述は終わりました。ウェブアプリケーションを作る場合には、CatalystArkなどのお好みのWAFを活用して、素敵なアプリケーションを作ってください。

モデルは一度作って終わりではなく、アプリケーションの一生を通して繰り返し成長し、かつ合理化していくものです。従ってその実装たるコードも変わっていくものですが、そのための作業が楽だとリファクタリングの心理的敷居が下がります。この些細なヘルパーモジュールが、モデル作成・保守作業の一助となれば、と思います。

なお、MySQL WorkbenchとDBIx::Class::Schema::Loaderの合わせ技は、冒頭に触れたYAPC::Asia 2009の特別研修の補講で教えていただいた内容のほぼそのままです。JPAのみなさん、どうもありがとうございます!

4. 実用上の留意点

趣味で開発中のプロジェクトの一つ(某MMORPG向けのERPやグループウェア)は80テーブル超に肥大化して、モデルもそれに関連してメタボ気味になりましたが、ドッグフードを食べてみたところ、モデルの海を自在に泳いで開発出来ている実感があります。

ただし、実用する際には幾つかの点に留意する必要があります。

4.1. 多対多の関係は未対応

一番の留意点は、多対多(N:M)の関係のハンドルはこれからサポートされる予定である、ということです。これはDBICx::Modeler側の0.005時点での制約です。多対多の関係を透過的に扱いたい場合には、自分でロールを作って皮を被せるか、DBICx::Modeler自体を拡張するか、いずれかの対処が必要となります。

4.2. スキーマ全てにモデルが必要ではない

上述のスライドでdannさんが提唱されている通り、必ずしも全てのスキーマをモデルでラップする必要はありません。エンタープライズアプリケーションというのは、処理の少なからぬ割合がDBの単なるCRUD処理であることも珍しくありません。であれば、無駄に一つ層を設けるよりも、素直にそのままスキーマクラスを取り扱った方が、パフォーマンス面で優れるのみならず、開発生産性や保守性も優れることがあります。層を分かつ原理主義に陥ることなく、現実との距離感を適切に保って、妥協して行きましょう。

ただ、あまり早期から最適化を進めるのも考え物です。端から箸にも棒にも掛からない状態は論外ですが、それはまずい実装に起因することが多いはずで、層を分けた事による極端な性能劣化はあまり起き得ません。まずは綺麗な設計を推し進めてみて、それで性能面の顕著な問題が生じた場合にのみ、以降の保守開発に支障を来さない範囲で、最適化を図れば良いでしょう。

4.3. 独自ツールへの不安?

こうした開発効率化の錦の御旗は、あっけないほどに容易に切り裂かれることがあります。

真っ先に思い付く「“どこの馬の骨とも知れぬ”ツールを業務に使うことはまかりならん」、という例のアレについては、MySQL公式のツールなので、安心感があります。Oracle, DB2, Symfoware, HiRDBなどではなくMySQLを使える現場であれば、説得は比較的容易でしょう。

次に、「標準外のツールを使うのはまかりならん」という問題。社用PCには間違いなく入っているExcelと違い、遍在していないツールの使用は敷居が高いことがあります。私もチーム内でVisioを使って、良い顔をされなかった経験があります。

私はExcel方眼ドキュメント否定派ですが、基本要件など、顧客(ユー子ならば親会社)と協同する作業工程であればむしろExcelを活用すればいいと思います。一方でER図に顧客が手を入れる場面というのもあまり考えにくいので、ここは開発側のみで閉じた作業と位置付けて、独自ツールを使う論陣を張りたいところです。

いずれにせよ成果物としてER図の画像やテーブル定義書も生成出来るので、それを納品すれば良いことになります。

「うちはどうしてもExcelで納品しなければいけないんだ」という場合であっても、ER図であれば画像をそのままExcel文書に貼り付ければ良いですし、テーブル定義書もXML文書を(XML::Parserなどで)パースして、その結果を基に(Spreadsheet::Writeなどで)Excel文書を生成すれば事足ります。

追記: Standard版ではHTMLで出力出来ます。Community版は`*.mwb`をunzipして`document.mwb.xml`を扱う手があります。

同じ作業をツールをまたいで何度も行う雇用創出的な仕事に従事することほど辛いことはありませんよね。

4.4. MySQL Workbenchにこだわる必要はない

ここまでMySQL Workbenchを猛プッシュして来ましたが、それ以外のツール(たくさんあります!)でも同様のことは出来ます。

DDLスクリプトを出力出来ること、ないしはDBに直接反映出来ることという条件さえ満たせば、DBIx::Class::Schema::Loaderによるスキーマクラス群の生成以降の流れが一緒になるからです。

DB定義書やER図を素のExcelで(オートシェイプと表組みを駆使して)定義しているのでない限り、現在お使いのツールと組み合わせて、各現場なりの創意工夫で作業手順をカスタマイズすることも容易だと思います。

ディストリビューションに同梱した例ではSQLite用のDDLスクリプトを手書きしてしまいましたが、これはDBICx::Deployを利用して、スキーマクラス群を元にSQLiteのDBを生成する方が自然だと思います。

追記: [SQLite出力用のプラグイン](http://www.henlich.de/software/sqlite-export-plugin-for-mysql-workbench/)があります。

4.5. ドッグフードの範囲は限定的

私はDBICx::Modeler::Generatorをいくつかのプロジェクトで実用していますが、それはあくまで日曜プログラミングでの範囲に過ぎません。エンタープライズアプリケーションと連呼しておきながら恐縮ですが、勤め先ではPerlを使った案件が殆ど全くないので、本当の業務アプリケーションへの適用事例はまだありません。

何事もそうですが、DBICx::Modeler共々、作ろうとしている(もしくは保守しようとしている)アプリケーションの特性を良く考えて、

  • モジュールの利用が開発生産性および保守性に寄与するか
  • それらの利点が欠点と比べてなお有用か

を、冷静に判断することが求められると言えるでしょう。

4.6. 怪しげな名前空間

DBICx::Modeler::GeneratorDBICx::Deployと同様に間違い易い名前空間にいますが、それには

  • DBIの拡張(e*X*tension)としてDBIx::Class)があり、
  • それはしばしばDBIC(「でぃびっく」と読むようです)と略され、
  • さらにその拡張としてDBICx::*という名前空間がある

という理由があります。これはDBICx::Modelerのヘルパーモジュールである以上、宿命として諦めました。

5. 中身の話

さて、中身の話は無味乾燥になりがちですが、少し補足しておきます。

5.1. MooseとOrochiで拡張容易性を確保

Mooseの他、lestrratさんが15日目に紹介されたDI(dependency injection, 依存性注入)フレームワークであるOrochiを利用しています。

DIをまだまだ十全に使いこなしているとは言えないのですが、DIのおかげで疎結合な構造に出来たので、レゴブロックのように部品を組み替えることが容易です。このモジュールではごく一般的な要件にのみ対応した枠組みしかご用意していませんが、特殊用途への対応部分を肉付けすることも比較的容易なので、一つの実装参考例としてご覧頂ければ、と思います。

5.2. 獅子搏兎気味なオーバーキルモジュール

上述の通り、既に一発スクリプトが色々あるくらいですので、わざわざモジュール化するまでもないかも知れません。少なくとも、MooseやDIパターンを使わなくても十分書ける単純な処理であることは確かです。

正直なところ、鳩を撃つのに豆鉄砲でなく大砲を持ち出すような感もあります。ですが、このヘルパーモジュールが必要となりうる場面は、テーブルが満載のエンタープライズアプリケーションを作ろうとする場面だと認識しています。

また、そもそもこのモジュールはコードジェネレーターであるため、本質的に開発者向けのものです。生成されるコードと違って、このモジュール自体は本番環境(production environment)には配備(deploy)しません。

従って、依存モジュールについてはある種の開き直りに基づいて、楽に書けるものを利用させていただいています。

5.3. でもバッドノウハウです

myfinderさん18日目に紹介されたDBIx::Encodingについて、ご本人はBK(バッドノウハウ)だと謙遜されています。一方、DBICx::Modeler::Generatorは正真正銘のBKです。

DBの内容から生成したスキーマクラス(Aとします)に、それ以外の内容のみを記述した同名のクラス(A'とします)の内容を付加して、最終的なスキーマクラス(A''とします)を生成するというのがDBIx::Class::Schema::Loaderの一つの流れです(上述のZIGOROuさんの記事の様に、もう一つの流れもあります)。

この流れを実現するために、Class::Unloadでクラスを一旦アンロードした後に@INCを追加し、再度(最初とは別の場所にある)同名クラスをロードしています。

use lib指定をし直して2度スクリプトを実行するということと本質的には同じで、ライブラリ探索パスの動的な切り替えのために別プロセスのperlを使い分けるという方法よりはましとはいえ、これはかなり気持ちが悪い処理です。

5.4. モデルクラスは単なるコードジェネレーターとして生成

モデルクラスのコード生成にあたっては、人気のテンプレートエンジンText::MicroTemplate::Extendedを利用しています。

であればスキーマクラスのコード生成もDBIx::Class::Schema::Loaderに頼らず、DB解析結果の情報だけ拝借して生成する手もあるかと思いました。

上述のZIGOROuさんの記事でも、

あるいはSchema::Loader自体に手を加えるかですかね。

という記述があります。しかし、現状で既に満足出来るワークフローになっているので、深追いはしないことにしました。

6. まとめ ~ Perlはエンタープライズアプリケーションに向いています!

以上、casual track的な内容で恐縮ですが、DBIx::Class::Schema::Loaderのようにスキーマクラス群とモデルクラス群を一発生成する、DBICx::Modeler::Generatorのご紹介でした。

Perlの内在思想として最も有名なのは"There's More Than One Way To Do It"(TMTOWTDI, 方法は一つじゃない)ですけれども、"Easy things should be easy, hard things should be possible"(簡単なことは簡単に、難しいことも可能に)というものもあります。

PerlはLLの代表格なので、軽薄短小で小粋なシステム向きだと思われることがあります。しかしPerlは、Javaの専従分野と思われがちな重厚長大なエンタープライズアプリケーションでさえも開発出来るのです。「も出来る」というと、何か痩せ我慢をして無理にPerlを使っている後ろ向きな感触を受けかねませんので、さらに踏み込みましょう。Perlはエンタープライズアプリケーションでこそ開発生産性や保守性が華開くのだと。

例えば本稿でご紹介した要件・モデリング・実装までを乖離なく貫くことが出来る仕事の仕組みは、“エンタープライズアプリケーションをアジャイルに開発する”という新鮮な体験の一端をもたらしてくれます。DBひとつ作るのに、延々と続く会議や、山ほどの依頼書や、実装としばしば噛み合わない仕様書……等々が必要な仕事に閉口している方は、是非一度体験してみてください。

言語とは言語単独で評価すべきものではなく、便利な外部モジュールであるとか、確立された自動化テスト環境基盤の総体として評価すべきものではないでしょうか。『モダンPerl入門』にも記載されていますが、Perlではそれらが豊富に揃っているのです。

日本に於いて、そして世界に於いて、エンタープライズ分野でのPerlの存在感が増すことを願ってやみません。あなたもPerlで日本の情報システム産業を事業仕分けしてみませんか? ……ただしあまりに切り込みすぎて不興を買うこと(実話)を避けるためにも、上司や同僚への進言はくれぐれも慎重に!

to be continued...

さあ、JPerl Advent Calendar 2009 hacker trackも、遂にゴールが見えてきました。あと3日間です。明日23日目を執筆してくださる方を絶賛募集中です!