RoadMovie

write down memos or something I found about tech things

【第2章】 ApplicationServiceの導入

※ この記事は「Railsのリファクタリングに立ち向かうための教科書」シリーズの第2章になります(3/5)

 【序章】 問題はどうして起こるのか ~ 方針とアーキテクチャについて
 【第1章】 ModelとServiceを紐解く
★【第2章】 ApplicationServiceの導入
 【第3章】 マイクロサービスを見越した実装
 【最終章】 リファクタリングを通してチームを強化していく


今回はApplicationServiceの導入について説明し、さらにリファクタリングを進めていきたいと思います。おそらく、第1章までを進めていくと、ある疑問にぶつかることになるかと思います。それは、「誰がModelとServiceを使ってユースケースを実現するのだろう?」ということです。Controllerにやらせますか?そうすると再びFatControllerに戻ってしまいます。本来Controllerの責務はroutingにより受け取るリクエストに応じてレスポンスを返すことです。そこでユースケースを組み立てて、Modelを作ってServiceを呼んで・・というのをやっているのは責務上良くないです。この問題点を解決するのがApplicationServiceになります。ApplicationServiceの責務はずばりドメインユースケースを組み立てること」。これがうまくいくと、エンジニアじゃない人がコードを読んでも、そのユースケースで何を行うのかということを理解できるようになります。


例えば下記はあるアプリケーションのapplication_servicesです。

├── bank_setup                                                                                                            
│   ├── activate.rb
│   └── sepa_builder.rb
├── contract_classify
│   ├── accept.rb
│   ├── cancel.rb
│   └── decline.rb
├── payment_charge
│   ├── payment
│   └── transaction
└── signup
    ├── bank_information
    ├── complete.rb
    ├── create_consultation_protocol.rb
    └── personal_information

契約を結ぶ際に銀行口座を登録したりする必要があり、その後サインアップが完了します。上記のディレクトリ構造を見ると、どういうドメインがあって、それぞれのドメインでどういうユースケースがあるのかというのが明白ではないでしょうか。


では、具体的にこの中から1ファイルを選んで見てみましょう。 signup/complete.rb を見てみます。これは名前の通り、サインアップが完了した際のユースケースを表現します。

module Signup
  class Complete

    def self.call(customer)
      instance = new(customer)
      instance.call
    end

    ...
    
    def call
      create_consultation_protocol
      notify_success_signup
      notify_success_signup_internally
      update_contract_status
    end

    private
    
    def notify_success_signup
      Notifier::Email.call(
        'signup_success_notification', customer.attributes
      )
    end

    ...
end

一部省略してありますが、インスタンスメソッドのcallを見ればこのユースケースで何を行うかが明確かと思います。プログラミングは自然言語で実装できますので、ユースケースを的確にメソッド化していくことで、非エンジニアがここを読んでも処理を理解することができるようになります。こうすることでコミュニケーションコストはかなり減りますし、メンテナンス性は格段に上がります。


また、privateメソッドでServiceを呼んでいることにも注目してください。publicメソッドから呼ばれるインスタンスメソッド以外は、Serviceを呼んだりModelをinitializeしたり、queryを発行したりして、ユースケースの実現を担うことになります。こうできることでControllerはApplicationServiceを呼ぶだけでいいということがわかりますね。


なので、(何度も見せますが)下記の図のように、ApplicationServiceはControllerが受けるリクエストに応じて、アプリケーションの実装を使ってユースケースを実現するエントリーポイントになるということがよくわかったかと思います。 f:id:mr7myself:20200729222711j:plain



いかがでしたでしょうか。ここまででRailsリファクタリングを進める手立てとして、十分な知識を習得できたものと思います。次回「【第3章】 マイクロサービスを見越した実装」では、少し視点が変わるのですが、ヘキサゴナルアーキテクチャの中で出てくるport/adapterという考え方を用いて、マイクロサービスなどの外部サービスとの連携を見据えたリファクタリング方法をご紹介したいと思います。マイクロサービスで開発をしていなくても、Slack通知など、外部サービスとの連携がある場合は有用な考え方なので、ぜひご一読ください。

それでは!