Grailsでビジネスロジックをどこに書くか(GrailsとDIとAUTOWIREと)
Grailsの入門者が(←俺)、ひととおりGrailsのチュートリアルを終わって、つまりBookmarkなりBookなり、Authorなりのドメインモデルとビューの生成の仕方がわかって、さて、次の段階に行こうと思ったときには、なんらかのロジックを書こう、ということになります。
一般に、Grailsでは、ビジネスロジックの記述場所としては、サービスクラスのメソッドが第一候補になります。具体的には、grails-app/services/配下に、クラス名が「Service」でおわるGroovyクラスとして書き下していきます。
これらのサービスクラスのインスタンスは、コントローラのプロパティとして、SpringのDI機能(AUTOWIRE)を使って、どしどし自動ディペンデンシーインジェクションされます。具体的には、サービスクラス名の先頭を小文字にした名前のプロパティをコントローラのフィールドに設定すると、newをせずとも、serviceのシングルトンとしてアクセスできます。
たとえば、
grails-app/services/WikiService.groovy
class WikiService { String format(src) { src.replaceAll("<", "<") : } }
こんなサービスクラスを定義しておき、Controllerでは
grails-app/services/PageController.groovy
class PageController { def wikiService def show = { String formattedText = wikiService.format(page.body) : } }
みたいにサービスクラス名(WikiService)の先頭を小文字にしたプロパティ名(wikiService)を定義しておけば、newせずともDIコンテナSpringによって生成されたWikiServiceのインスタンスが使えるというわけです。
なお、この場合、WikiServiceのインスタンスはSpringでいうsingleton scopeで生成されています。Grails 0.6からは、scopeとしてsingleton以外に、"prototype", "singleton", "request", "session", "flash", "flow" and "conversation"などの指定が可能とのことです。
面倒なDIコンテナの設定を、うまく隠しこんでいるといえるのではないでしょうか。
ちなみに、
class PageController { def wikiService :
ではなく
class PageController { WikiService wikiService
にすると、WikiServiceのソースを変更したときにインスタンスの再設定・再インジェクションのあたりで失敗し、grailsの再起動が必要になってしまいますので、型指定なしにするのが吉です(経験上。他の回避法があるかも)。
なお、動的言語とDIコンテナの関係は、興味深いトピックのひとつだと思いますが、Grailsはひとつの具体的な解を示していると思います。またこれが、Railsとの主要な差異のひとつでもあるのではないでしょうか。(規約によってオブジェクトの関係が設定される、という意味で、CoCはDIの一種(自動DI)であるともいえるのでしょうけれども、DIが完全には消え去らないところがGrailsらしいというかSpringらしいところ)。
参考リンク
ちなみに、現バージョンではコントローラクラス中だけではなく、domainクラス中のプロパティにも自動インジェクトされるはずが、0.6でなぜかうまくいかない。どうしてだろう。