こんにちは。エンジニアの安達です。最近はギフティのeギフトの商品性を拡張する新規プロダクトの開発に取り組んでいます。
先週のKaigi on Rails 2024に参加して、Vladimir Dementyevさんと五十嵐邦明さんの発表内容を聞いて、Railsでのバリデーションロジックの書き方について、今までモヤモヤとした課題感があった部分のリファクタリング方針が決まった話を書こうと思います。
"Rails Way"にフォーカスした初日の基調講演からの流れ
初日のDementyevさんの基調講演から、その次の枠の五十嵐さんの発表は、ともにRails Wayに沿ったソフトウェアアーキテクチャ設計をテーマにしていて、発表の趣旨に近いものを感じました。また、実装の具体例として「フォームオブジェクト」が登場した点にも一致する部分がありました。
Dementyevさんの基調講演は、Rails Wayの思想や哲学としての考え方の話題が多く、五十嵐さんの発表では、より実務に即した具体的な実装手法の話題が多かったので、両者を連続して聞くことで、Rails Wayと、その具体的な実装への落とし込みについて理解を深めることができました。
基調講演で語られたRails Way
基調講演の『Rails Way, or the highway』では、Rails Wayの思想とは何か、またRails Wayに沿った抽象化レイヤーの導入はどのように行うべきか、というテーマが語られました。
Rails Wayとは、Railsアプリケーションを構築する哲学のことで、提供する価値としては、「生産性と幸福のための最適化」「意思決定からの解放」「スケーラビリティを考慮した設計」があり、具体的な設計の考え方として「MVC」「設定より規約」「複雑さの圧縮」があると紹介されました。
その一方で、シンプルなMVCではビジネスロジックの複雑さに対応し切れず、ファットモデルのような課題が発生するという、おなじみのテーマにDementyevさんは話を進めます。
Dementyevさんはこの問題を、Railsの「おまかせ」では埋められない「隙間」と表現し、そこを埋める際に、開発者がRails Wayを無視してしまうと、Rails本来の生産性と幸福度が失われる「怪獣 on Rails」のような事態に陥ると警鐘を鳴らしました。
その後、Rails Wayに沿った抽象化レイヤーを導入する方法について、4つの指針の紹介があり、抽象化レイヤー導入の具体例として「フォームオブジェクト」の実装例が紹介されました。
五十嵐さんの発表
五十嵐さんの『Railsの仕組みを理解してモデルを上手に育てる』では、モデルを中心にビジネスロジックを組むのがRails Wayに沿っているという見解(サービスレイヤの導入には否定的な立場が強調されていました)を前提として、具体的な設計手法の解説がありました。
前半では「イベント型モデル」の考え方が紹介されました。一般的に想起されるようなRailsのモデルだけではなく、「行為を記録するモデル」としてのイベント型モデルに着目すると、モデルを主体にした設計で扱える範囲が広くなるという話だと理解しました。
後半では、モデルを分割する具体例として、バリデーションロジックをDBのテーブルと密結合なバリデーションとは別に用意する必要が出た場合に、フォームオブジェクトとして分割できる手法が実装例を交えて紹介されました。
発表を聞いた後にその場でチームの仲間とディスカッション
この2つの発表を聞いていて、私の中で、自分のチームのプロダクトのバリデーションロジックの課題をRails Wayに沿って改善するアイディアが整理されて行く感覚がありました。
今回のKaigi on Railsには私と同じ開発チームからもう一人参加していて、同じく五十嵐さんの発表を聞いていたので、発表後すぐにその場で改善アイディアについてディスカッションしました。会話したメンバーも、このバリデーションロジックの課題を日頃から考えていたので、お互いにビジョンはすぐに一致して、5分ほど会話して改善方針の大枠が決まりました。
その後チームに持ち帰って、今週になって再度詳しくディスカッションし、チケット起票から最初の改善実装のPull Requestの作成までスピーディに進めることができました。今はコードレビューで方針のすり合わせをしています。
バリデーションロジックの課題
私のチームでは、RailsをAPIモードで利用(フロントエンドアプリケーションをRemixで実装)し、新規開発フェーズのプロダクトを開発しています。
今回改善方針が決まったバリデーションロジックの課題は、バリデーションロジックの実装方式が複数あり、方針が曖昧になっている、というものです。
現状では、以下の3つの種類のバリデーションロジックが存在します。
- モデルでのバリデーション
- json_schemer gemによるリクエストパラメーターのバリデーション
- サービスレイヤーでの個別ロジックによるバリデーション
決まった改善方針は、このうちモデルでのバリデーション以外は廃止とし、複数モデルにまたがるバリデーションの要件に対しては新たにフォームオブジェクトを導入して対応する、というものです。この改善が入ることで、使い分けが曖昧な複数のバリデーション手法を、ActiveModelのバリデーションロジックの書き方に集約できる狙いがあります。
バリデーション手法が混在した経緯
背景として、バリデーション手法が混在した経緯について書きます。
json_schemer gemは、定義したスキーマに従って、Rubyのhashとして渡したパラメーターのバリデーションを行えるgemです。最新のバージョンのJSON Schemaまで対応しているとされています。
開発当初は、APIモードを利用していることもあり、このgemによるリクエストパラメーターのバリデーションを主体とする構想でした。しかし、後から管理画面の開発をする段階で、モデルでバリデーションしたい要件が出現し、方式が混在した経緯です。
この状況に対して、モデルに寄せた方が良いアイディアはありつつも、ビジネス要件は現状実装で満たせていることもあり、全体的な書き直しに踏み出す意思決定ができていない状態でした。
そんな中で聞いた今回のKaigi on Railsの冒頭の基調講演で、Dementyevさんの発表に「これは恐らく私の昔のコードのことだ:考え抜くの代わりに、隙間を埋めるために人気のあるGEMなら何でも持ち込もうとしていた」という言葉がありました。json_schemer gemは素晴らしい機能性のgemです。その一方で、使い手である私たちに迷いがある状態で、gemの機能にもたれかかっていることに問題があると気づきました。
その後、五十嵐さんの発表で、フォームオブジェクトの具体的な実装手法と、DBテーブルに紐づくモデルのバリデーションとの使い分けについて学べました。
2つの発表を聞く中で、私の中の迷いは、自分なりのRails Wayを追求してみようという決意へと変わっていました。
Railsでのソフトウェアアーキテクチャ設計について
最後にまとめとして、私が今理解しているRailsでのソフトウェアアーキテクチャ設計の考え方について書きます。
今回のKaigi on Railsでも語られたように、Railsでのソフトウェアアーキテクチャ設計では、Railsらしさを考慮することが重要と思われます。その一方で、今回の発表で触れられた「レイヤードアーキテクチャ」や「フォームオブジェクト」はRails固有の考え方ではありません。開発者は、一般的な設計手法への理解を深めつつ、RailsらしさやRubyらしさとの調和を図る必要があります。また、設計手法の良し悪しは、チームメンバーのスキルや志向、プロダクトの性質によっても大きく左右され、正解がありません。
チーム内でよくディスカッションし、RailsやRubyらしいコーディング体験の楽しさを保ちつつ、ビジネス上の拡張性にも強いアプリケーションを書いて行きたいと、思いを新たにしました。