2019/3/22に開催されたRails Developers Meetup 2019に「7年目を迎えたRailsアプリケーションの傾向と対策」というタイトルで小川が登壇させていただきました。今回は発表にあたって考えていたことや、スライドに盛り込めなかった補足の内容についてご紹介させていただきたいと思います。
はじめに
エンジニアの小川です。去る3/22にRails Developers Meetup2019にて「7年目を迎えたRailsアプリケーションの傾向と対策」というタイトルで発表をさせていただきました。
スライドは以下となります
発表内容は、キッチハイクで数年開発をしてきた中で、試行錯誤しながら取り入れてきたRailsのレールから「ちょっと外れた」パターンについての紹介でした。
今回の記事では、上記の発表内容について考えていたことや、スライドに盛り込めなかった補足事項などをご紹介させていただきたいと思います。
発表にあたって考えたこと
レールを「ちょっと外れる」
今回の発表ではRailsのレールを「ちょっと外れる」がテーマでした。
わざわざ「ちょっと」をつけているのはRailsのよさをなるべく活かしたかたちでレールを外れたいなあと思っているからです。
有名な「設定より規約」にあらわれているように、Railsは意図的に割り切った設計を取り入れることによって、簡単に素早い開発ができるようになっています。そんな設計思想を無視して重厚なアーキテクチャを取り入れてしまえば、Railsのよさは失われてしまいます。
とはいうものの、コードベースが大きくなってくるとRailsのレールだけでは厳しい場面がでてきます。
そんなときにレールをちょっと外れた程度のパターンであれば、Railsのよさを損なわずに、しかもコードの見通しをよくしながら、楽しく開発が続けられるのではないか・・・そんな思いが念頭にあります。
実例集がもっとほしい
実はレールをちょっと外れたパターン集は既に定番といえるべき記事がいくつかあります。
そのうちのひとつがスライド内でも触れた「肥大化したActive Recordモデルをリファクタリングする7つの方法」です。
既に6年以上前に公開されている記事ですが、今読んでも古びておらず、ここで紹介されている7つのパターンは、実際に取り入れていたり、そうでなくても耳にしたことがあるという方も多いのではないでしょうか。
しかしながら、これら定番パターンを現場で適用してみようという段になると、見通しが悪くなってきます。
理由はこうした定番パターンを扱ったソースの絶対数がまだまだ少ないことによります。いざ適用しようとしても参考にできるソースが少ないため、実装時のハードルが上がってしまうのです。
であればどうするか。参考にできるようなソースの絶対数を増やしていくほかありません。
そんなわけで、実際の現場に適用してみてよかったことだけでなく、失敗なども含めての事例紹介をしてみよう・・・、そうすることで一人でもこれらのパターンにトライしてみる方が増えるといいな、というのが今回の発表にあたって考えていたことでした。
定番パターンたちの補足
それでは、発表でご紹介した3つのパターンについて、補足の説明をしていきたいと思います。
View Model
こちらのパターンは、以前にブログの記事として紹介をしていました。
この View Model
のパターンは、原理は非常にシンプルで、単純にビュー内に書かれたロジックをPORO(ないしはgem)で置き換えていくだけです。
名前と意味に気をつけよう
スライドではサラッと流してしまっていますが、 View Model
はとかく名前周りで混乱が起きやすいパターンなように思います。
View Model
,Decorator
,Presenter
など同じような(あるいは重複した)概念を指しているが名前が違うMVVM
パターンのように同じView Model
の名前でも役割が違う
後述する Service Object
もそうですが、こうしたアーキテクチャパターンの話では、上のような同じ概念で名前が違う / 同じ名前で意味が違うものがしばしば出てきます。
ついついこのことを忘れて、自分の前提としている用語やその意味で話をしてしまいがちですが、こうした意味や名前の違いに意識的でないと、実装メンバーがもやもやした気持ちを抱えながら実装することになりかねません。
できればチーム全体で予めこの用語が何をするか、というのを整理しておくとスムーズに開発ができると思います。
View ModelからのDBアクセスは避けよう
なお、以前に記事を書いた際に、はてなブックマークで Presenter
のコード例でのDBアクセスについてコメントをいただきました。(コメント、本当にありがとうございます)
これは全くそのとおりで、 View Model
内での永続化層へのアクセスはおすすめしません。コメントいただいたとおり、View Model
は表示に関する責務のみを担うべきだからです。
別の箇所で永続化層へのアクセスをおこない、 View Model
では以下のコード例のようにコンストラクタでオブジェクトを渡すなどがよいかと思っています。
class ProductPresenter def initialize(user, product) @user = user @product = product end def price discount? ? @product.discount_price : @product.price end def discount? @user.paid_member? && @product.member_only_discount? end end
Form Model
Form Model
は AcitveModel::Model
といっしょに
Form Model
はRailsに限ったパターンではありませんが、Railsで使うならば ActiveModel::Model
との併用がおすすめです。
理由は form_with(form_for)
との相性がとてもいいからです。includeするだけで、通常のformのコードをほぼそのまま使えるので非常に便利です。
なお、 ActiveModel
とフォームの相性の話はこちらの記事で知りました。
上記のお話以外にも、Railsで使える定番Tipsが詰まっていて本当に参考になります。
また、 Form Model
にフォーカスした記事としては、こちらが決定版というべき素晴らしい内容です。
まだ読んでいないという方はぜひご一読いただければと思います。
Service Object
Service Object
に用いられている「サービス」という概念は言語問わず使われる非常にメジャーなパターンです。Rubyのみならず、JavaやC#を使われている方にも馴染みがあるパターンではないでしょうか。
濫用されがちがな「サービス」
しかし、この「サービス」はあまりに汎用的な言葉すぎて濫用されがちなパターンでもあります。特に意識しなければ、「複数の処理をひとまとまりの手続きで扱いたいときに使う」ような曖昧なものになってしまいます。
さらに、サービスがどのように定義されているかを見ていると、とにかく幅広く、これもサービスの語の濫用に拍車をかけているように思えます。
スライドでも触れましたが、Eric EvansのDDD本での定義からして既にレイヤー毎にサービスが分かれています。かなりざっくりとしたまとめですが、それぞれの役割を簡単にまとめると以下のようになるかと思います。
名称 | 役割 |
---|---|
アプリケーションサービス | ・ドメインサービスやインフラストラクチャサービスの呼び出しをまとめあげてひとまとまりの処理をおこなう |
ドメインサービス | ・ドメインオブジェクトでは収まりが悪い重要なビジネスロジックを含む処理をおこなう |
インフラストラクチャサービス | ・外部へのメッセージ通知など純粋に技術的な処理をおこなう |
上記の定義から分かるように、これらは同じサービスの名前であっても意味するところが全く違います。
さらにMartin FowlerのPofEAAでも同じように「サービスレイヤ」が定義されています。ここでの「サービス」はドメイン層を呼び出してひとまとまりの操作を実行するインターフェースを提供する、DDD本でいうアプリケーションサービスに近い役割を担っています。
有名どころの定義だけでもこれだけ意味合いが違うものを、あまり意識せずにサービスの一言でくくってしまえば、話がかみ合わなくなりそうです。
このような様々な「サービス」を適切に扱うには、 View Model
と同じく、じぶんのいう「サービス」が何を指すのかを明文化しておくことが必要かと思います。
スライドではSerivce Object
をDDD本でいうアプリケーションサービスに則った形で紹介しました。ただ、実際のところ、キッチハイク内でも、これらに統一できているわけではなく、インフラストラクチャサービスや行き場に困った処理クラスが混在している状況です。この点はキッチハイクで取り組みを進めて、改めて記事にしたいと思っています。
まとめ
最後に、全体的な観点から二点補足をしたいと思います。
パターンの名前と意味をすり合わせておく
Service
や View Model
であげたように、パターンをめぐる話では人によって同じ名前でも想定している意味が違ったり、違う名前で同じものを指している場面にたびたび出くわします。
しかしながら、これらがみんなの納得するようなかたちでひとつの意味に統合されるのは望み薄です。であれば、人によっては違和感を感じるかもしれないけれど・・・と前置きした上で、せめてチームの中で統一した意味として合意しておくのが、チーム内でのよりよい議論のために大切だと思います。
Railsのレールを外れない
逆説的ですが、今回ご紹介した内容はRailsのレールを外れないためにこそ大切なことかと思っています。
個人的には極力Railsのレールに乗って開発するのが非常に大事だと考えています。しかし、それで行き詰まったときにどうするか。そこで重厚なアーキテクチャにいきなり移行するのはちょっと違うかなと思うのです。Railsのよさを打ち消してしまうことになりかねないからです。
ですので、最初はRailsのレールに忠実に開発をしてみて、どうにも苦しくなった・・・、そんなときの選択肢として、ご紹介したパターンを検討してみるのがいいのではないでしょうか。
おわりに
以上、Rails DM2019での発表にあたって考えたことと、発表した各パターンの補足についてをご紹介いたしました。
今回発表した内容が現場のコードの改善、ひいてはRailsにとってよりよいパターンの議論のきっかけになるのであれば、それ以上にうれしいことはありません。現場のコードでちょっと気になる、適用ができそうなところから少しずつ始めていっていただければと思います。
We're Hiring!
キッチハイクでは、React Nativeエンジニア・Railsエンジニア・フロントエンドエンジニアを募集中です!