📘

『Sustainable Web Development with Ruby on Rails』はRails使ってるなら絶対面白いと思う

2022/05/20に公開

David Bryant Copelandの『Sustainable Web Development with Ruby on Rails』を読んでいますが、この本めちゃめちゃ面白いですね。

Sustainable Web Development with Ruby on Rails

https://sustainable-rails.com/

Railsの設計で悩んだことのある人なら絶対読んで損はないというか、共感したり反発したりにやにやしたりで楽しめると思います。RailsというかWebアプリ開発の歴戦の勇士(正直あまり若くなく、つらい経験を重ねてきた生き残り的な人)が語るベストプラクティス感があります。

本書の構成

大きく3部構成です。

    1. Introduction

その名の通り導入です。本書の目的、Railsのアーキテクチャの紹介と、ビジネスロジックの話など。

「Sustainable」とは何か? という説明もここで書かれています。これはつまり「持続可能」という(そのままの)意味ではありますが、仕様やリソースが増えたり変わったり、チームのメンバーが増えたり変わっても持続可能、ということでした。「スケーラブル」でも良かったけど別に大規模で超早い、みたいな話ではなかったので違う単語にした(意訳)という意図っぽいです。

    1. Deep Dive into Rails

本書のメインです。Railsの各パーツについて、どう使えばいいのかを紹介します。

    1. Beyond Rails

Railsそのもの以外の話として、認証認可、API、CIや運用(監視やロギング等)についてです。

価格について

(大事なポイントなので先に持ってきました)

「正直$49.95はちょっと高い」という人は、International Pricingというところを読むと良いです。日本などのUS以外の国から買う場合のディスカウント(クーポンコード)が書いてあります。

ちなみにamazon.co.jpでも売っています https://www.amazon.co.jp/Sustainable-Web-Development-Ruby-Rails-ebook/dp/B08ND6X37Q/ こちらは元々日本向け価格になっているようですが、PDF+EPUB+Markdown(!)が入手できる公式サイトから購入するのをおすすめしたい気もします。

本書の特徴

Railsには逆らわないが、そのまま使わない

Railsはopinionatedなところがあるというか、気の利いたアーキテクチャーや設計ポリシーをRails界隈の外部から持ってくるとだいたい食い合わせが悪いので、大改造をするか、あるいは微妙な折衷案を狙うか、という印象があります(※個人の主観です)。

で、本書はどういうスタンスになっているかというと、

  • 基本的にはRailsの長所を最大限認めて、それに合わせる
  • が、全面的に準拠はせず、ある程度の取捨選択をしつつ、違う技法を取り入れる
    • ただしクールだったりクリーンだったりする技法は導入せず、枯れたものを採用する

という扱い方をしています。

レールから外れるところは例えば以下になっています。

  • ActiveRecordのvalidationにはデータの整合性をとるための機能は期待せず、DBの制約を適宜使う
    • なぜなら抜け道があるため(それはそう)
    • CHECK制約も場合によっては使うが、必ず使うというほどではない
  • マイグレーションはRubyではなくSQLを使う
  • ActiveRecordやActiveModelにドメインロジックは書かない、代わりにサービスクラス(!)、サービス層を導入する
    • そして原則としてコントローラ・ジョブ・メーラからActiveRecordを呼ばない
    • ただしこのサービスクラスはドメインサービス限定とかではなく、どちらかというとドメインモデル or トランザクショナルスクリプトぽいところもありそう
    • 結局ActiveRecordは便利なデータアクセス層的な雰囲気もある(共通化したいクエリみたいなのは書く)
    • もっともassociationも書くし、validationもユーザー(UI)向けに割り切った上で普通に書く
    • ActiveRecordのコールバックは限られた用途のみ使う(before_validationでのデータ正規化とか)
  • ActiveJobは使わず、Jobライブラリ(例えばSidekiq)を直接使う
    • ApplicationJobクラスは作るけど中で直接include Sidekiq::Workerを書く
  • Helperも限られた用途のみ使う(グローバルなUIとか)

現場叩き上げ感あふれる設計方針

本書のスタイルは地に足がついている感があるというか、かっこいいパターンや流行りっぽい手法は全力で無視して、枯れた設計・アーキテクチャを最大限駆使するスタイルに見えます。
著者がアンチクリーン・アーキテクチャ&アンチSOLID派であることは明確にしています。だいたい「オブジェクト指向らしい」とか「ファンクショナル」とか「クリーン」とか「エレガント」とか「再利用」とか「デザインパターン」とか「メトリクス」とかがキライ、というか何度も痛い目にあったと吐露しています(そしてそれらの代わりに「理解しやすい」「変更しやすい」を持ち出し、重視します)。

かといってあまり教条主義的というか極端な立場にもならず、何ごとも「トレードオフ」であることはたびたび触れています。そのスタンスが上記の「Railsには逆らわないが、そのまま使わない」というポリシーに反映されているようです。

本書の大きなポイントはビジネスロジックにあるのですが、ここは無料で読めるサンプルでも触れているので、気になるのはここだけ読んでもいいかもです。

どちらかというとドメインロジック層とデータアクセス層(やや厚め)を分けて、ドメインロジック層をデータアクセス層に依存させてる形ですね。クリーンアーキテクチャ等とは逆向きの依存関係というか、ある意味でPofEAAに戻ってるような印象です。Rubyは柔軟な言語なのでそこまでIoCする必要はないでしょ感があり、それはそうかもという気もします。

もっとも、ドメインロジック(ビジネスロジック)層はドメインモデルなのかトランザクショナルスクリプトなのかは触れていないように見えました。実際そこにはそれほどこだわりはないのかもしれません。むしろ、以下のような理屈により、とにかくActiveRecordのモデルから複雑なビジネスロジックを剥がしたい(でも便利なDBアクセス機能はそのまま使いたい)、ということが詳しく説明されます。

  • ビジネスロジックは複雑で、変更も大きい
  • 複雑で変更の大きいものがあちこちから依存されるとバグが深刻化しやすく、システムが不安定になる
  • ActiveRecordのモデルはあちこちから参照されることが避けられない
  • よって、ActiveRecordのモデルにビジネスロジックを置くべきではない

なるほどですね、という感じです。アーキテクチャのあるべき姿を追求するのではなく、とにかく目の前の課題を解決しながらシステムを無事に成長させる、開発を持続させることを最優先にしています。

  • DDDとかは気になるけど、ActiveRecordがあるのにわざわざrepositoryパターンとかは使いたくない(ActiveRecordをそのままリポジトリとして使うには限界があるが、自前でrepositoryを書いてもActiveRecordよりも劣化したものにしかならない気がする)
  • でもレイヤーは分けたい(なんでもかんでもARのモデルに書くのは辛いし、統制が取れない)

とかいう人にはある程度以上納得できそうなポリシーです。

ちなみにサービスクラスの設計方針も興味深いです。

  • クラスメソッドを使わない(WidgetsCreator.create_widget(.....)とかせずにWidgetsCreator.new.create_widget(.....)で呼び出す)
    • .newのたった4文字を省略するメリットは特にない、毎回書けばいいだけ(それはそう)
  • Foo.callのような汎用的なメソッド名をつけない
    • やりたいことが複数できたときに困るため(それはそう)
    • .callとかで呼び出せてうれしいのは生成と実行のタイミングが分かれるコマンドパターンだが、Railsでの普通のサービスクラスはコマンドパターンのように後で呼び出したりしないので意味がない
  • なんでもコンストラクタインジェクションで外部から渡したりせず、必要であればメソッド内で生成してもよい
  • サービスクラスの実行結果を返すResultクラスを作り、モデル等を返す際にはそれでwrapする
    • ただし汎用的にせず、各サービスクラスごとに用意し、それぞれに固有のメソッドを実装する(!)
      • このResultクラスは枯れた知恵っぽくて面白いです。一瞬Result型みたいなやつかな?と思いましたが全然違いました。

そしてそれぞれについて、なぜそうするのかを都度具体的に説明しているので、著者の意見を取り入れるかどうかはそれを読んで納得できるかどうかを読者の方で判断することもできます。

その他の特徴

疲れてきたので箇条書きにします。

  • 例としてOpenStructをちょくちょく使っているのが興味深い
  • CSSではデザインシステムの重要性をまず説いて、デザインシステムがあればOOCSSかfunctional CSS(TachyonsとかTailwindCSSとか。著者はTachyons使いだったらしい)を選ぶ
  • ログは一行で意味のある形にしたいのでLogrageを使う
  • テストはMinitestを使ってるけどRSpecも否定しているわけではない(単に説明の都合なのかも)。ちなみにfixtures派ではなくFactoryBot派でした
  • HTMLテンプレートはERBです。理由は「Railsのデフォルトだし、HTMLはみんな知ってるので、特に強いメリットがない限りERBで十分(そしてhamlやslimのメリットはそこまで大きくない)」とのこと
  • Rakeタスクは1ファイル1タスクに分割すると探しやすくて便利
  • モノリスvsマイクロサービスとか、JAMStackの話もあるけどまあ読んでみてください

著者について

書きそこねていたので補足しますが、本書の著者はStitch Fixでプログラマーからアーキテクトまでやった後、別のスタートアップのCTOをやってる人で、『Agile Web Development with Rails 6』の共著者の1人でもあります。キャリアの最初はJavaからだったそうなので、本書の枯れてる感の源泉はその辺りにもあったのかな…と思わないでもないです。
あと、Brutalist Web Designのサイトの人でもありますね。このBrutalist Web Designの雰囲気も、どこかしら本書と通じるところもある気がします。

ちなみに過去の発表資料では、RubyConf 2019での「The fewer the concepts, the better the code?」が面白かったです。言語の機能を駆使したコードと、基本的な機能だけを使って素朴に書いたコードのどちらが「better」なのか? という考察です。

https://speakerdeck.com/davetron5000/the-fewer-the-concepts-the-better-the-code

これも本書に通じるところがありそうです。

まとめ

なんだかんだ言って、著者の言うこと全部に対してそっくりそのまま共感する人はそれほど多くないかと思います。例えば私ならマイグレーションはSQLじゃなくてRubyでいいじゃん…と思っています(あるいはいっそ別ツールにするかも)。

共感したとしても、いきなり本書に従ってもりもりコードを大改造するよりも、Railsで開発をしているチームのメンバーとか、Rails使っている知り合いと、本書を読みつつRailsアプリの設計をどうするべきか、それはなぜか、という議論をするのが一番良い使い方かもしれません(知り合いがいなかったらブログでも書いてみるとか。って、私がいろんな人の感想が知りたいだけなんですけど)。いやまあ大改造して試してみて、大成功したり大失敗したりするのも手かもしれませんけどね。

というわけで、興味があればぜひどうぞ。

追記

先日まで行われていたRailsConf 2022でも本書の著者によるtalkがあったようで、発表資料が公開されていました。

YOUR SERVICE LAYER NEEDN'T BE FANCY, IT JUST NEEDS TO EXIST

名前のとおりサービス層についてで、だいたい本書の内容(の一部)と共通してるようなので購入する前にこれを見てから判断しても良さそうです。

「サービス層」「サービスクラス」というと辛い思い出が甦ってきてそっ閉じしたくなる人もいそうですが、本書の「サービスクラス」は結局「ふつうの(ただの)Rubyのクラス」なんですよね。そしてそれを置く層を「サービス層」と呼んでるだけのようです。

要は、

  • Rails標準の(サブ)クラスだけで頑張ろうとするな
  • ビジネスロジックはちゃんと分けよう(そしてテストも書こう)
  • 変に共通のスーパークラスとかを作る必要もなくて、普通に使いやすいRubyのクラスをそれぞれ書こう

くらいの穏当な感じなので、そこまで毛嫌いせずに受け止めてもいいんではないかと思います(が、それだけだと自由すぎるので、自前でルール化・パターン化してちゃんとやらないとやっぱり統制取れなそうではあります。そしてそれが難しいんですよね…)。

Discussion