update.はじめに夏なのに家で冷房を入れることを禁止されているどうも鈴木です。コントーラーやモデルが肥大化する話はRailsを普通に使っていると誰しもが直面する話だと思われます。Userモデルのように、色々なところから参照されるモデルは地獄みたいなことになってしまいます。地獄は家だけにしてほしいですね。世間ではサービス層だなんだと言われていますが、何となくapp/servicesなんてディレクトリを作り満足し、後で負の遺産とか言われるのはとても辛いです1。そもそもRails使ってるのはレールに乗るためなので、なるべく賢い人が作ったレールに乗って生きていくのが正義だと思っています2。いうgemを使い始めたので、ここで少し紹介します。元気よく本番で動いてくれていますよ。Trailblazerとは?(出典:http://trailblazer.to/)Trailblazerは、特定のアーキテクチャに沿って書くためのgemで、RailsやSinatraなどと一緒に利用できます。この記事ではRailsと一緒に使う想定で説明を行います。Trailblazerは設計が非常によく考えられており、学習コストはかかりますが結果的にRailsを普通に使うのに加えを以下のようなメリットが発生します。リファクタリングしながら使い始められるTrailbalzerの開発者曰く、ローマは一日にしてならずらしいです。Controller問題が発生しないチーム内でのコード水準のばらつきが抑えられる既存のRailsコードと共存できたり少しずつ移行できるようになっているのは、導入の際にとても助かります。複雑なコードほどTrailblazerを使う意味があるのですが、解説が難しくなるので、比較的シンプルなコードで解説したいと思います。self).deliver_nowendこれは見ての通りPostを保存するコードで、以下のような処理の集まりになっています。新しいPostのオブジェクトを生成し、post変数に入れるpostにユーザを紐付けるpostをバリデーションするDBに保存する保存できればユーザに通知を飛ばし、indexに戻る保存できなければnewのテンプレートを表示するTrailblazerでは、上記のような処理の1つ1つをStepと呼びます。これらStepを管理するためのクラス(Operation)を別途作り、ControllerやModelとは分離してしまうのがTrailblazerです。下でもう少し詳しく説明していきます。OperationTrailblazerの主要な概念として、Operationがあります。Operationは名前の通りで、上で列挙したStepをOperationする(上から実行してく)ための仕組みです。先のコードをOperationにすると、以下のようになります。endend雰囲気で読めなくもないですが、1つずつ解説していきます。Contract(Reform)まずは上半分、Contractです。Parametersとモデルのバリデーションを、Contractという場所で合わせて書きます。ContractはReform::Formクラスを継承したものなのですが、このFormという名前が全てを表現しています。どの項目をどのように検証したいかは、モデル単位で考えるのではなくフォーム単位で考えるのがTrailblazerの考え方です。以下の『規約に同意』のチェックのバリデーションのように、「モデルのacceptanceでやるのって正直気持ち悪いな...」と思いながら書いてしまっている箇所が数か所あるとおもいます。モデルに書いてしまっていたりしたのですが4、フォームの構造とモデルが密結合した状況になっていて、思えばモデルとは何だったのか感が出てしまっていました。画面都合のものはモデルに書きたくありません。このような気持ち悪さを、Trailblazerはモデルではなくフォーム単位でバリデーションすることで解決します。endendモデルからバリデーションが無くなり、作成時のフォームの要素定義と同時に、バリデーションも記述するようになりました。気持ちいいですね!考え方が分かったところで、話を戻して実際のバリデーションしているコードを見ていきます。}外からの入力は、propertyで書いた項目しか値が入りません。Parametersのpermitと同様の働きをしてくれています。Assignmentも安心です。その項目に対してバリデーションをします。これは見れば分かると思います。今回はContractを、量も少ないので(あとは説明を簡単にするため)Operationの中に書いてますが、別のファイルに書いてOperationの外に出すこともできます。大規模なフォームでもかなりシンプルに書くことができます。Step次に下半分のStepです。通知を飛ばすOperationの真の力は、超簡潔なStepのフロー管理にあると言っても過言ではありません。次の図のように、Operationは2つのレーンを持っていて、上から処理が実行されて行きます。右のレーンには成功の、左のレーンが失敗の処理が乗ります。(出典:http://trailblazer.to/gems/operation/2.0/api.html#flow-control)処理は右のレーンから始まります。右レーン上にある各Stepが成功する限り、右のレーンを走り続けます。Stepが失敗すると、失敗(左)のレーンに移動され、成功(右)のレーンに戻ってくることはありません。Step最後の:notify_author!は、元々Postモデルにあったメソッドです。このコンテキストからしか呼び出しがないメソッドなので、Stepとして移動してきました。このように普段はモデルに生やすメソッドをOperationのStepとして書くことで、モデルの肥大化を防いでいきます。Controllerからの呼び出しさて、Operationが定義できたのでControllerから呼び出して見たいと思います。:newendとてもシンプルに書けて良い感じです。まとめTrailblazerでは、Controllerの処理をメソッド単位でOperationに分割します。バリデーションに関する処理をContractにまとめ、そのほかの処理をStepとして記述します。強力なStepのフロー管理により、様々な条件分岐を簡潔に書くことができます。Trailblazerのドキュメントにより詳しい説明やサンプルコードがあるので、興味を持った方はそちらも見てみてください。実際にStepを書いてみると分かるのですが、実はモデルに書くべきことというのはほとんどなく多くの処理はリクエストに依存していたのだなというのが、ここ数ヶ月Trailblazerを利用して思った感想です。モデルにはhas_manyなどのAssociationと、あとはscopeを書くぐらいで、ビジネスロジックは全てOperationに移動できます。OperationはStepのまとまりとなっており、処理の流れを追うのがとても簡単です。2〜3ヶ月前に書いた自分が書いたコードが暗号と化している、なんてことも無くなります。影響範囲がOperation単位に閉じているので、仕様変更した時に変なところが壊れている不安もかなり減りました。Operationはそれぞれが非常に簡単に実行できるので、テスト時にPostモデルを使いたいなと思った時に、factoryを利用しなくても本番で稼働しているプログラムでデータが作成できます。"本文")上のFactoryGirlで作成する場合は、(ちゃんとその処理を書いてあげないと)メールは送られません5。一方で、下のTrailbalzerのOperationを実行した場合はStepに含まれるものが全て実行されるため、もちろんメールの送信も行われます。実データとfactoryが違う問題、または実データに近づけようとfactoryに色々書かないといけない問題から離脱することもできました。何より嬉しいのは、われわれ利用者が知恵を絞って設計を考えなくても、決まった場所にコードを置いていくことでそれっぽく様々な恩恵を受けられるという点です。Inc.採用を仲間集めに。upLoginユーザーは見つかりませんでした。