てくすた

ピクスタ株式会社のエンジニア・デザイナーがつづるよもやまテクニカルブログです

Rails サービスの事業拡大期におけるビジネスロジックを書く場所と書き方

この記事は Pixta アドベントカレンダー 2017 の 7日目の記事です。

こんにちは、昨年末に株式会社ピクスタに入社したエンジニアの大村です。てくすたに書くのは2回目になります。前回はペアプロについての記事でした。 当時は PIXTA のチームにいたのですが、現在は fotowa の開発チームに移って奮闘中です。 今回は、その fotowa チームでの開発プロセス改善の一環で行った、ガイドラインの策定について紹介しようと思います。

fotowa とは

気軽にプロのカメラマンを予約して、ハイクオリティな写真を撮影できるサービスです。サービスのピークは11月の七五三です。撮影事例はこちらをご参照ください。 f:id:urakawam:20171130211403p:plain

fotowa の開発チーム体制は

2016年2月にサービスイン(開発は2015年から)して以来、順調に事業とシステムを拡大しています。

そんな中、初期から開発を引っ張ってきた id:necojackarc がワーキングホリデーに行ってしまい、フルタイムコミットから離れる(現在ワーホリ先のカナダからリモートのパートタイムで参加してくれています。そんなリモート事情は13日目にて語られるかと)ことになりました。

このタイミングで僕が fotowa チームに入り、また、その後の開発体制の拡充もあり、現在 fotowa チームは、エンジニアがフルタイム3人、パートタイム2人の5人体制(パートタイムの一人以外は半年以内に join)になりました。

このようなチームの拡大に伴い、1人で全部手を動かす開発体制を脱却し、チームとしてきちんと動けるよう、「増えた人が即戦力になるための仕組みが必要!」ということになりました。

で、

いろいろな施策を実施のしていますが、そのうちの一つに、「コーディングのガイドライン」の制定があり、今日はこれを紹介します。

「コーディングのガイドライン」とは

fotowa では Ruby on Rails という web アプリケーションフレームワークを利用してシステムを構築しています。

Ruby on Rails というフレームワークが、「統合システムを個人で高速に開発する」ことを指向していて、実際プロジェクト立ち上げ期には有用です。 しかし、サービスが拡大し、メンバーが増えるにつれ、 Rails の規約と、明示されていないコーディング基準だけではコードの質と開発速度を両立するのが困難になっていくことが予想されます。

また、「自己組織的なチーム」が理想ですが、入ったばかりのアルバイトなどが入れ替わりながら関わる以上「寄せ集め」になる瞬間が必ずあります。それを「戦う集団」にしていくためにはディシプリンが必要になります。ディシプリンと言ってしまうと大げさですが、自己組織化プロセスに終わりはない ので、その助けになる、チームの方針・決まりごと・ガイドラインという形式で、チーム内で目線を合わせ、各メンバーが同じレベルの仕事をしていくための基準を作っていくことになります。

チーム内で議論の末、開発プロセスの改善・新規メンバーの教育のためのツールの一つとして、ガイドラインを明示・共有して行くことにしました。

ガイドラインにないこと

  • いわゆるコーディングスタイル規約
    • Rubocop の設定として全社統一基準が存在します。
  • ネーミング規約
    • 今のところ Rails の一般的なルールから逸脱してないという認識が共有されているので、特にありません。

ガイドライン(抜粋)の紹介

Rails に逆らわない

とはいえ、 Ruby on Rails というフレームワーク自体が強烈な規約とデフォルト設定によってコーディングスタイルが明確に設定されています。そのため、基本的には、一般的な Rails のベストプラクティスを適用していきます。

Rails の規約に則った上で、「デフォルト設定を抜け出してチームとしての正解を目指す際に、このガイドラインが必要になる」という最も基本的な方針を最初に書いてあります。

薄いコントローラ、Fatでなく見通しの良いモデル

本稿の本題になる「ビジネルロジックを書く場所と書き方」のため、詳しく書きます。

暗黙のサブリソースを考慮したルーティングとコントローラ

よりリソース指向な設計を維持するために、 操作するデータの単位をリソースとして扱い、Rails のデフォルトの7つのアクションメソッドをなるべく使用するようなルーティングを目指します。

具体的に例示すると、 UserController に update_password メソッドを用意したくなったら、 Users::PasswordController に update メソッドを実装しましょう、ということです。

ActiveRecord クラスの複雑性を下げ、役割を明確にするためのクラス群

いずれのクラスも、単一責任の原則に基づき、太らないように記述の基準を定めています。

ValueObject

値が等しければ等しいと見なされるデータと、そのロジックの単位を作りたい場合に使います。 fotowa では例えば、スケジュールなど時間に関するデータが各 ActiveRecord に存在しますので、そのロジックを共有するところで使っています。

FormObject

フォーム入力をラップし、1つのフォーム送信で複数のActiveRecordを更新したり、複雑なバリデーションを行う場合に使います。

ServiceObject

複数のActiiveRecordにわたって操作が行われる場合や、ActiveRecordの操作に伴い、外部サービスとの連携が行われる場合、ActiveRecord の操作方法に strategy パターンを導入したい場合などに使います。

サービスの実装は手続きプログラミング的なコードになりますが、何も考えずになんでも Service にすると太ります。そのため、単一責任の原則に従い、ActiveRecord や ValueObject が持つべきロジックを ServiceObject に持たせないこと、入力のバリデーションはコントローラ(と FormObject)がすること、など細かいお約束があります。

FinderObject

ActiveRecord の複雑な検索をまとめるときに使います。 ActiveRecord の操作(書き込み)が行われるときは Service 、読み込みだけが行われる場合は Finder という使い分けになります。

QueryObject

ActiveRecord の scope などが複雑化した場合、独立させるために使います。

ViewObject

表示にしか使わないロジックを ActiveRecord から隔離するために使います。 draper という gem を使って実装しています。

f:id:urakawam:20171130210134p:plain 雑な説明図ですが、それぞれの役割が伝わりますでしょうか?

ディレクトリ構成
app/
┣ controllers
┣ decorators
┣ finders
┣ forms
┣ models
┣ services
┣ values
┣ queries
┣ …

services や values は(それらもモデルなのだから) models のサブディレクトリに置くべきという意見もありますが、私たちのチームでは「素のRailsへの近さ」を重視してこのようなディレクトリ構成を指向しています。

まとめ

「ビジネスロジックを書く場所と書き方」を置く場所は?という問いの答え

コントローラ及び ServiceObject に「ビジネス上何をするか」、ActiveRecord 及び ValueObject に「モデリング上どう表現・実現するか」をそれぞれの明確な責務に則って書きます。

それを決めてどうなるの?

ガイドラインを用いてチームの目線を合わせることで、チームメンバーを早くチームになじませてチームの開発能力を向上させます。

  • 従来のコードを読みやすく、新規のコードを書きやすくします
  • 個人の好みより適切なレビュー基準になります
  • 延々とコードレビューが通らない(誰も幸せにしない)事態を避けます
  • 設計の基準の最低ラインが高まり、コミュニケーションの効率がよくなります。

まとめのまとめ

  • 人が増えると、コミュニケーションコストがバカにならないので、最低限のドキュメントも大事になってくるよ。
  • まだまだパワーアップする fotowa にご期待ください。そして、よかったら使ってみてください。今なら、クーポンコード「texta2017」で2000円引き!(2018年3月末日の撮影まで)