RailsでWebアプリを作成する際に導入して良かった設計やgem

Rails で Web アプリを作成・運用していく中で、個人的に導入して良かった設計や gem を紹介していきます。
なお、本記事では特定のユースケースに絞った話ではなく、比較的どの Rails アプリでも利用できるような内容を書いていければと思いますので、参考になれば幸いです。

## Form オブジェクト

基本的には Rails Way、MVC と Rails 標準機能に沿った設計でシンプルに保つようにしてますが、Form オブジェクトについては導入しています。  
特定のユースケースの処理(ビジネスロジック)をモデルから分割する目的で使用し、今回は自分がよく利用する ActiveRecord モデルを継承する方法を紹介します。  
下記のようなイメージになります。

```ruby:app/models/user.rb
class User < ApplicationRecord
  ...
end
```

```ruby:app/forms/user_registration_form.rb
class UserRegistrationForm < User
  # モデル(テーブル)には存在しない属性を入力値として受け取って処理する際に attribute を利用
  attribute :xxxxx, :string

  # 特定のユースケースのみ必要な validation を定義
  validates :yyyyy, presence: true, length: { maximum: 20 }

  # 特定のユースケースのみ必要なコールバック処理を定義
  after_create :zzzzz

  ...

end
```

ActiveRecord モデルを継承する利点は、ActiveRecord で提供されている機能を利用できることや共通で必要な validation や処理をモデル側に記載でき DRY にできることです。  
そして特定のユースケースに必要なビジネスロジックは、継承した Form オブジェクト側にまとめることができ、モデル側に記載した共通で利用したい処理も継承した Form オブジェクトで利用できます。

例えば、会員機能を持つアプリの場合は、新規登録・退会・会員情報の更新などそれぞれのユースケースに応じて、入力値や必要な validation、コールバック(通知やロギング、他モデルに影響する処理など)が異なることが想定されるので、各ユースケースに応じて上記のような Form オブジェクトを定義するイメージになります。

またコントローラー側からは、通常の ActiveRecord モデルと同じように利用できます。

```ruby:app/controllers/users_controller.rb
class UsersController < ApplicationController
  def create
    @user = UserRegistrationForm.new(user_registration_params)
    if @user.save
      render status: :created, json: {}
    else
      render :error, status: :unprocessable_entity
    end
  end
end
```

## RuboCop

Ruby の静的コード解析ツールおよびコードフォーマッターです。
規約に違反したコードを警告するだけでなく、自動で直してくれる機能もあります。
設定は任意にカスタマイズできます。

https://github.com/rubocop/rubocop

チーム内で、共通のコーディングルールを設けることで可読性や品質の向上に期待できます。
現在携わっているプロジェクトでは初期から導入していたこともあり、ほぼデフォルトの状態で始めて、利用していく中で厳しすぎるなというルールについて相談してカスタマイズしていく運用にしてます。
併せて、[rubocop-rails](https://github.com/rubocop/rubocop-rails)・[rubocop-rspec](https://github.com/rubocop/rubocop-rspec)・[rubocop-performance](https://github.com/rubocop/rubocop-performance)も利用してます。

また RSpec とともに CI で RuboCop のチェックもしています。

## Shoulda Matchers

ActiveRecord や ActionController などが提供している機能のテストをワンライナーで書ける　matcher のコレクションになります。
主に validation や association のテストを手軽に書けます。

https://github.com/thoughtbot/shoulda-matchers

```ruby
require 'rails_helper'

RSpec.describe User, type: :model do
  describe 'validations' do
    it { is_expected.to validate_presence_of(:last_name) }
  end
end
```

Rails が提供している validation 等のテストまで書くべきかという議論はありますが、個人的には spec を書いてる際に漏れに気づけたりするのと、shoulda-matchers を使えばかなりお手軽なので書いてもよいかと思えます。

## Committee::Rails

OpenAPI とアプリで実装されているレスポンスに乖離がないかチェックしてくれる gem です。

https://github.com/willnet/committee-rails

現在携わっているプロジェクトでは、フロントは Next.js + React + TypeScript で Rails は API Mode で利用しています。
API の仕様ドキュメントは OpenAPI 形式で記載していますが、実装とドキュメントで乖離がないメンテナンスされた状態にしたいと思い導入してます。

commitee-rails を導入すると `assert_response_schema_confirm` メソッドが使えるようになるので、各リクエスト Spec に1行追加するだけで、OpenAPI のスキーマ通りになっているかチェックできます。

```ruby
require 'rails_helper'

RSpec.describe UsersController, type: :request do

  describe 'GET /users' do
    it '200 を返却すること' do
      get users_path
      assert_response_schema_confirm(200)
    end
  end
end
```

## Alba

Ruby の JSON シリアライザーです。

https://github.com/okuramasafumi/alba

当初 JSON API を作る上で、jbuilder を利用してましたが、アプリの特性上 1 API で関連する複数モデルを preload して rendering するような API が多く partial が遅い問題に直面したため、パフォーマンス改善を目的に乗り換えました。
ベンチマークは省略しますが、約 100 ms ほど速度改善した API もありました。

クラスのインスタンスまたは配列(Rails の場合、主に ActiveRecord モデルインスタンスや ActiveRecord::Relation)を alba の Resource クラスに渡すという性質上、jbuilder ほど柔軟性はありませんが、JSON シリアライザーの選択肢の 1 つとして検討してよい gem かなと思います。