こんばんは。akitoshigaです。
先日参加したKaigi on Rails 2024で自分のRails力の至らなさを痛感したので、「Rails Way」について改めて自分なりに整理してまとめてみたいと思います。
最初に申し伝えとなりますが、この記事はKaigi on Rails 2024の以下のお二人の発表に影響を受けて書いたもので、その内容をかなり参考にさせていただいています🙏
Vladmir Dementyevさん『Rails Way or the highway』
五十嵐邦明さん『Railsの仕組みを理解してモデルを上手に捉える』
そのため、Kaigi on Rails 2024でこれらをご覧になった方は特に得るものがないかもしれません...🙏
前置きが長くなってしまいましたが以下本編です。
そもそも自分の何が至らなかったのか
振り返ってみると以下の3点につきると思いました。
元々設計は好きで色々勉強はしてきたのですが、振り返ってみれば中途半端に知識があったからこそRails Wayを軽視していたように思います。
また、自分が初めて触れたRailsのアプリケーションがすでにリリースから数年経っており、成長に伴いRails Wayから外れていたものでした。
そのため、この状態からRails Wayを遵守しようという意識にならなかったのも要因の一つだと思います。
『Rails Way or the highway 』の以下の一節が非常に重要だと感じました。
Rails Wayとは
『Rails Way or the highway 』では以下のようにまとめられています。
これを踏まえ、Rails Wayを自分の言葉で以下のように解釈しました。
「RESTを前提にリソース(URL)設計・データモデル設計を正しく行い開発者の設計判断とコーディング量を減らし生産性を上げる」
この解釈に至るまでには、ピクスタさんが運営しているポッドキャストの『texta.fm』が大変参考になりました。
text.fmの第3回『3.Low-Code Development』の中でRailsの思想について概ね以下のように述べられていました。
・シンプルなルールを仮定することで全体の作りをシンプルにする
・シンプルにすることで考えることを減らす
・考えることが減ればコードの量も減り、かつ設計に悩む時間も減る
・コードの量と考えることが減れば生産性は上がる
この思想の代表的な例はRailsのModelの実装だと思います。
RailsのModelは基本的にエンティティになります。
ご存知の通りModelはActive Recordパターンによりデータベースのスキーマと密結合しておりCRUD処理の窓口にもなっています。
その他にも、コールバックやバリデーションなどの仕組みを駆使してアプリケーションにおける複数の役割をModelに持たせています。
この密結合な構造と引き換えにRailsはシンプルなレイヤーを保ちつつ設計判断のコストを減らしています。
そのため、Modelを特定のユースケースと結びつけて付随する処理の殆どをModelに実装するのがRails Wayに沿った設計になります。
一方、サービスなど新しいレイヤーを追加するのはRails Wayに反しているということになります。
そして、このModelを有効活用するためにはRESTを遵守したリソース(URL)設計とデータモデリングが必要になります。
先述のtexta.fmでリソース設計、データモデリングについて以下のように述べられています。
- Railsは必要なデータは最新の物と割り切る事で殆どの事をデータのCRUDで表現可能
- 殆どの事をデータのCRUDによって表現できるとリソース操作もパターン化が可能
- パターン化できるとスキャフォールドと規約によるメタプロによって生産性が上がる
- データモデリングとリソース設計が決まればコーディング自体はある程度導き出せる
- そのためにはデータモデリングをしっかりやる必要がある
つまり、アプリケーションのユースケースをリソースとそのCRUD操作で表現できるようになれば、実装されるコードはスキャフォールドに近くなり非常にシンプルで生産性を高めることができます。
また、この他にもRailsは様々な規約を定めることで開発者のコード量・設計判断のコストを減らしています。
例えば、先ほどのModelはデータベースのテーブル名に基づく特定の規則に則ったクラス名にすることで、開発者がORマッピングの設定をすることなく特定のテーブルと紐づいたエンティティとして使用することができます。
以上がRailsの思想に則ったRails Wayを体現する方法だと知りました。
ただし、これには問題があります。
Rails Wayに沿ってどう拡張していくのか
先述の通り、Rails WayによってModelはユースケースと深く結びつくことになります。
アプリケーションが小規模なうちはこれで大丈夫なのですが、肥大化してModelが複数のユースケースと結びついた時にRails Wayが崩壊するという問題を抱えています。
これを回避するために以下の対応方法を取ることができます。
イベント型モデル
- リソース系とイベント系のデータが一緒になっていないか?
- イベントを見落としていないか?
- イベント型モデルを発見することができればユースケースと紐づけることができる
フォームオブジェクト
- パスワードの確認フォームなど、Modelと画面が1対1でなくなってきた時に使用する
- ActiveModelのモジュールをincludeすることでバリデーションやコールバックなどを使うことができる
PORO(Plain Old Ruby Object)
- 責務がはっきりしなかったり、永続化の必要がなくてもオブジェクトとして扱いたい概念が出てきた時に検討する
- Rails Wayからは外れそうになるためチーム内でのルールの共有などが必要
具体的なイベント型モデル、フォームオブジェクト、POROの詳細は初めに紹介した『Railsの仕組みを理解してモデルを上手に捉える』で解説されているので、気になる方は是非チェックしてみてください。
また『Rails Way or the highway 』ではRailsを独自の抽象化を定義しても以下の4層から外れないようにすることが重要だと述べられています。
まとめ
知ってしまえばRailsの思想は非常にシンプルですが、同時にモデリングの重要性といった本質的な奥深さも感じて以前よりRailsの事が好きになりました!
そして、自分もRails Wayに則した綺麗なアプリケーションを構築してみたいと思うようになりました🙌
今回自分なりにRails Wayについて考えてみましたが、自分の理解が及んでいないことも多分にあると思います。造詣の深い方がいらしたら是非この記事に関してフィードバックを貰えると嬉しいです🙌