uehaj's blog

Grな日々 - GroovyとかGrailsとかElmとかRustとかHaskellとかReactとかFregeとかJavaとか -

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でなぜかうまくいかない。どうしてだろう。