Railsはスパゲッティ、Grailsはピッツァ。
はじめにことわっておきますと、Railsについての知識は先日Rails寺子屋で見聞きした程度のものしか持ちあわせておりませんので、Railsに詳しい人からの意見も頂きたいです。
さて、とある後輩が、
Rails やっててビジネスロジックってどこに置くのかなー、って気になって調べてみたら Model に書くお作法っぽい。Grails は標準で Service (やsrc/groovy)があっていいですねー
なんてことを言ってました。
自分の認識としても、Rails は特に View において先進的な機能を取り込んでいて、先進的な画面のWebアプリケーションを作ることにフォーカスしてるんだろうなぁ、という感じを受けていました。Sprockets とか TurboLinks とかはその最たるものだと思うのだけど、ビジネスロジックの分離においては Rails4 の Concerns に至るまで
コードベースが大きくなるにつれ特定のコンテキストでしか使われないコードがActive Recordにボコボコと追加されていって見通しが悪くなっていくという問題はよく知られている
http://ntcncp.net/2012/12/26/translate-dhh-concerns-to-extract-module
みたいな問題が放置されてきた、っていうのは、たぶん「きれいなJava」の世界の住人からは驚きの対象になるんじゃないかな(Javaのウンコードはきっとあれなので「きれいなJava」と表現しております)。
View 志向ゆえに Model がスパゲッティ、なんて言葉が浮かんで、そこに生地をしっかり持ってる Grails はピッツァなのかな、なんて思ったのでした。上に乗せるものをいろいろと工夫すれば、Grails もすごく美味しく見えるはず!(触ったことない人もためしてみてよ、実際美味しいよ!)
Grailsベストプラクティス(和訳)
This article is Japanese translation of http://groovy.dzone.com/articles/grails-best-practices .
DZoneで公開されている Nitin Kumar 氏の記事を和訳したものです。また、「要約」の節を冒頭に移動しています。大元の記事はおそらくこちら。
類似の記事として、Amit Jain 氏がInfoQで公開している記事を uehaj さんが和訳したものもあります。
Grails Best Practices
Grailsは、アジャイルに基づいた迅速でインタラクティブな開発フレームワークです。 "設定いらずの規約" convention not configuration を支持しています。
この記事では、Grailsの使いかたと、Grailsを取り巻くベストプラクティスを説明します。
■要約
Grailsが目指すのは、アジャイルな方法での俊敏かつ迅速なWebアプリケーション開発です。Grailsは、CoC(設定より規約)とDRY(Don't Repeat Yourself)だけではなく、既存Javaコードの再利用も可能にします。俊敏かつ迅速なやり方で、堅牢で安定したWebアプリケーションを構築するための力が得られるのです。
■ドメイン駆動設計
常にドメイン駆動設計で:
まず基本的なドメインモデルのクラスを作成し、それらをオンラインで操作するための scaffolding を使います。これは、やる気を保ちつつ、あなたの作ったドメインを熟知していく役に立ちます。
テスト駆動でのアプローチを:
ドメインモデルのテストケースは、バリデーションをあれこれ試すのに最適な方法となります。
バリデーション:
ドメインオブジェクトを良い状態にしておくために validator を使います。カスタムバリデータを実装するのは難しくありませんので、自前で書くのを恐れることはありません。コントローラで怪しげなハックをするよりも、ドメインクラスで検証ロジックをカプセル化する方が良いです。
■Grailsの規約に従おう
Grailsの規約に従おう:
Grailsは規約駆動開発であり、それぞれの規約に従うことが望まれます。ビューはただビューであり、コントローラはただコントローラであるようにしましょう。
トランザクション境界としてのサービス:
トランザクショナルな部分は、サービスにまとめましょう。
ロジックの置き場所:
ロジックは、サービスやモデルオブジェクトの一部として実装しましょう。コントローラがリクエストを受けると、コントローラがサービスを呼び出すようにするのです。
■依存性注入
Grailsは、規約の上での依存性注入(DI)に基づいています。
適切な grails-app/folder に、決まった命名規則でコンポーネントを配置することだけを気にすれば良いのです。
配置規則:
サービスは services フォルダに、コントローラは controllers フォルダに。
命名規則:
ドメインクラス Xxx があり、それに対しコントローラやサービスが必要な場合は XxxController や XxxService と名前を付けましょう。命名規則に基づいて、Grailsが自動で結びつけてくれます。
■画面表示/ビュー
ページネーションを使おう:
多数のデータセットをページで区切ることで、優れたユーザーエクスペリエンスを作り、画面表示全体のパフォーマンスを向上させることができます。
カスタムタグを使おう:
再利用可能なUIのカスタムタグを作りましょう。全体的な開発の生産性を向上させ、メンテナンス性を高めます。
layout をスマートに使おう:
基本的な flash.message の表示は、個々のビューで表示するために繰り返し記述するよりも layout の中で扱うようにしましょう。
正しいJavaScriptライブラリを選ぼう:
理にかなうAjaxライブラリを選んでください。ライブラリのダウンロードには時間がかかるので、使うライブラリの数を最小限に抑えます。
規約にしたがって layout を使おう:
明示的なメタタグよりも、規約ベースのレイアウトを好みましょう。多くの場合、ある特定のアクションのための具体的なレイアウトは、メタマジック分岐を行うよりはるかに保守性の良い物を作ることができます。コントローラのためにページのサブセットを用意するときには便利なメタタグを活用し、ほかは規約による layout を使うのです。
configファイルの外部化:
常に外部化された設定ファイルを用意しましょう。運用において設定を書きなおす必要があっても、新しいwarファイルを生成せずに済みます。
動的 scaffolding を好こう:
静的 scaffolding よりも動的 scaffolding を好みましょう。
DataSorce設定ファイルを使おう:
DataSource.groovy でDB設定ができます。
カスタム validator を再利用しよう:
すべてのカスタム validator を共用 validator ファイルにまとめ、他のドメインクラスでも制約を再利用できるようにしましょう。
Web層にロジックを入れない:
プレゼンテーション層とビジネス層を明確に分離するために、Web層にロジックを多く置くことは避けてください。
BuildConfig.groovyを使おう:
アプリケーションに任意のプラグインをインストールするには、install-plugin コマンドを使うのではなく BuildConfig.groovy で宣言するようにしましょう。
ビューをシンプルにしておく:
できるだけシンプルなビューを作成し、ビジネスロジックにはサービス層を使いましょう。
テンプレートとカスタム taglib の再利用:
共通して使われる部分を template や g:render で分割し、共通のUI要素はカスタム taglib を使いましょう。
layout を使おう:
UI全体で一貫した外観になるよう layout を使いましょう。
DRY:
ビューを DRY("Don't Repeat Yourself") に保ちましょう。繰り返しあらわれるものは template に分割しましょう。
■コントローラ
コントローラは薄く:
コントローラの中にはビジネスロジック、Webサービス、DB操作やトランザクションを書かないように。コントローラは可能な限り薄くしましょう。コントローラの目的は、リクエストを受け入れ、ドメインまたはサービスを呼び出し、その結果をレスポンスとして返すことです。
コマンドオブジェクトを使おう:
フォームの送信にコマンドオブジェクトを活用しましょう。ただバリデーションのために使うのではなく――トリッキーなビジネスロジックをカプセル化するにも便利です。データバインディングを理解しましょう。Grailsのデータバインディングは、オプションが豊富で、器用です。
適切な命名規則を使おう:
"<DomainClass>Controller" の標準的な命名規則を使いましょう。
flash スコープを使おう:
flash スコープは、(入り組んだリダイレクトをしているとしても)ユーザにのメッセージを返すのに理想的です。
エラーオブジェクトを使おう:
バリデーションメッセージを表示するには、ドメインクラスのエラーオブジェクトを使いましょう。自分のアプリケーションのユースケースに関するエラーメッセージを作るときには、リソースバンドルが活用できます。
フィルタを活かそう:
URLやコントローラアクションの組み合わせに基づいて選択的にバックエンドのロジックを実行するような場合、フィルタを活用しましょう。
■サービス
サービスは、複雑なビジネスロジックや粗粒度のコードを置くための良い候補です。必要に応じて、サービスのAPIを簡単に RESTful/SOAP なWebサービスとして公開できます。
トランザクションのために使おう:
サービスは、デフォルトでトランザクショナルです。メソッドのいずれもがデータベースを更新しない場合には、非トランザクショナルにすることもできます。
コードの重複を避けよう:
共通の操作は抽出し、アプリケーション全体で再利用しましょう。
ステートレス性:
サービスはステートレスである必要があります。ステートフルな動作にはドメインオブジェクトを使います。
■ドメイン
setter と getter をオーバーライドしよう:
プロパティの挙動を変えるために setter と getter のオーバーライドを活用しても良いでしょう。
(※訳注:副作用のある getter を作ってしまうなど、使い方によってはベストプラクティスどころかバグの温床になります。使い方には注意しましょう。)
複雑なクエリは分解しよう:
複雑なクエリを用意するときには、処理の流れに沿って名前付きクエリを組み立てましょう。
ドメイン固有のロジックを制限しよう:
オブジェクトに置くロジックは、そのオブジェクト特有のものだけにしましょう。複数のオブジェクトを扱う、より複雑なビジネスロジックはサービスに置きます。
モデルのドメインロジックはドメインオブジェクトに置こう:
サービスにドメインロジックを置いてしまうと、永続化層の荷物をかかえるかたちになります。
domainフォルダに異物を混ぜない:
domainフォルダの中に、ドメインクラスでない一般的なユーティリティクラスや値オブジェクトを混在させないようにしましょう。そういったものは src/groovy に置くべきです。
賢明にコンストラクタを使おう:
意図せぬ初期化を避け、あるべきオブジェクトを構築するために、ドメインオブジェクトをインスタンス化する際には賢明にコンストラクタを使いましょう。
(※訳注:mass assignment脆弱性のようなデータインジェクションを避けるために、new DomainClass(params) のような暗黙のコンストラクタの使い方は避けましょう、明示的に初期化に使う値を指定するか、余計なことをしないコンストラクタを宣言しておきましょう、という話でしょう。)
■TagLib
TagLibはシンプルに:
タグをシンプルにしておくため、必要に応じてタグを再利用可能なサブタグに分けましょう。
ロジックを含める:
TagLib は、レンダリングよりもロジックを多く含むべきです。
複数のカスタム TagLib を使おう:
構造を良くするためならどんどんカスタムTagLibを作りましょう。
■プラグイン
再利用可能なプラグイン:
個別にテストできる再利用可能で独立したGrailsのプラグインとして、アプリケーションの機能性とロジック部分を構築しておくと、メインのアプリケーションから複雑さが取り除けます。
公開プラグインリポジトリ:
いろいろなアプリケーションで再利用可能なプラグインは、公開プラグインリポジトリに掲載されるかもしれません。
fixtures プラグインを使おう:
開発中、起動時にデータを用意するために。
少しの変更はプラグインを上書きしよう:
使うプラグインにちょっとした変更を加えたい場合、そのちょっとした変更のためにプラグインを展開することになりますが、代わりに同じディレクトリ構造またはパッケージにクラスを配置することで、特定のファイルを上書きしたかのように使うことができます。
onChange を使おう:
メソッドとプロパティがリロード後も保持されるように、自作のプラグインで動的なメソッドやプロパティを追加したい場合、onChange を使いましょう。
ローカルプラグインリポジトリを使おう:
ローカルプラグインリポジトリは、複数のアプリケーションでプラグインを共有するなど、いくつかの目的が果たせます。
大規模・複雑なアプリケーションのモジュール化:
大規模・複雑なアプリケーションをモジュール化して、懸念を分離するためにもプラグインが使えます。
プラグイン用の機能テストを書こう:
信頼できるプラグインになるよう、機能テストを書いておきましょう。
■テスト
テスト駆動開発で:
Grailsは、機能性がテストによって担保されるよう、TDDのテストファーストのアプローチを支持します。
まめにテストを:
テストによって、どこかがおかしくなっても速やかなフィードバックが得られやすく、すばやく修正できます。これにより、開発サイクルもスピードアップします。
テストカバレッジを使おう:
テストカバレッジを維持し、テストカバレッジの隙間を放置するのは極力避けましょう。
インテグレーションテストよりもユニットテストを:
実行/デバッグが早いだけでなく、より良い形での疎結合が強制されます。例外はサービスのテストで、インテグレーションテストが全般的に有効です。
継続的インテグレーション(CI)を使おう:
いろいろな環境でのビルドとデプロイを自動化するために、継続的インテグレーション(CI)を使いましょう。
テストには Grails console を使おう:
Web UIにGroovyのコンソールを埋め込んで Grails アプリケーションのテストするための console プラグインというものがあります。実行中のアプリケーションの内部を調べるのに非常に有効なツールです。
■デプロイ
release プラグインを使おう:
独自Mavenリポジトリに独自プラグインをデプロイするなら、release プラグインを使いましょう。
継続的インテグレーション(CI)を使おう:
チーム内での変更がマージされたときにあらわれるバグを検出できます。1人より多い人数のチームではほぼ必須でしょう。
プロセスを自動化しよう:
反復的なタスクを自動化するスクリプトを書きましょう。エラーを減らし、全体的な生産性を向上させます。
resource プラグインに馴染もう:
静的リソースの処理のための resource プラグインに馴染みましょう。
GGTS 3.2.0.RELEASE + Grails 2.2.1 での困った挙動ちゃん!
GGTS(Groovy/Grails Tool Suite) 3.2.0.RELEASE と、それに同梱されている Grails 2.2.1 を使ってておかしな動作になる!非常に困った挙動ちゃんを見つけてしまって本当にもう GGTS ったら。。
- ドメインクラスを変更して generate-all / generate-controller / generate-views を実行しても、変更がScaffoldコードに反映されない。フィールド追加時にも、フィールドが追加されていないコントローラ、GSPが生成され続ける。
→ Grails 2.1.4 を使うか、refresh-dependencies コマンドを打つと回避できる。
→ アプリを起動しなおさないと表示に反映されない。
これすごく不便なんですけど、挙動から察するに GGTS のバグというよりは Grails 本体側の挙動の変化の影響を受けてる感じなんですかね。。