Rails: アプリで最も重要な"境界"とは(翻訳)
このところ、もっぱら前回の私の記事でも触れた作業が続いています。Rails 4.2からRails 5.0へのアップデート、そしてRails 5.1へのアップデートの計画を立案していて、アプリケーションをフレームワークの内部詳細に癒着させないことがどれほど重要かが痛いほど身に沁みました。
癒着を切り離すことの重要性を示すために、Rails 4.2からRaisl 5.1にかけてActionController::Parameters
で行われた変更を見てみましょう。
🔗 Rails 4.2
Rails 4.2のActionController::Parameters
は、RubyのコアクラスであるHash
クラスのサブクラスだったので、他のハッシュと同じように使え、受け渡しも楽でした。
🔗 Rails 5.0
Rails 5.0で大きな変更が発生しました。 ActionController::Parameters
がHash
のサブクラスではなくなり、完全に別クラスとなったのです。この変更はセキュリティの強化とマスアサインメント脆弱性防止のために実装されました。しかしmissing_method
フックを使えばハッシュのすべてのメソッドを引き続きActionController::Parameters
で利用できました。
🔗 Rails 5.1
missing_method
フックがRails 5.1で削除されてしまいました。しかもActionController::Parameters
と通常のRubyのハッシュが切り離されて、#each
や#map
や#each_key
や#each_value
といったメソッドはActionController::Parameters
でもう使えなくなりました1。
Railsバージョンごとのpublic APIは、以下のように大変な変わりようです。
私たちが取り組んでいるアプリでは、コントローラからService層が呼び出されています。このService層は一部のビジネスロジックを担当しており、結果の多くはデータベースで永続化されます。
運の悪いことに、このService ObjectがActionController::Parameters
で初期化されていたのです。
そのあちこちで、Hash
を継承しているかどうかのチェックが行われていました。
# ...
return results unless hash.kind_of?(Hash)
# ...
これらはさらにストレージ層にまで渡され、Active Recordのserialize
メソッドでシリアライズされていました。
class Attachment < ActiveRecord::Base
serialize :data, Hash
# ...
end
Railsのアップグレードに取りかかろうとすると、既存のテストが落ちるようになり、ActiveRecord::SerializationTypeMismatch
エラーが大量に発生しました。
一見シンプルなRailsのアップグレードだったのに、ActionController
モジュールの内部と癒着していたせいで、途方もない時間がかかることが判明しました。こうした問題を解決するには、ドメインロジックとフレームワーク内部との境界を越境しないようにしておくことが重要です。
フレームワークのオブジェクトをドメインロジックに渡すときはくれぐれもご注意ください。Ruby標準の型か、独自のValue Objectを使うようにしましょう。
境界で仕切ることで、多くのメリットを得られます。
- 柔軟性が高まる
- 境界で区切ることで、アプリ全体を書き直さずにフレームワークやライブラリ、場合によっては言語すら切り替え可能になります。
- テストしやすくなる
- ドメインロジックをフレームワーク内部から切り離すことで、複雑なセットアップや依存関係なしにコア機能を楽にテストできるようになります。
- メンテナンスしやすくなる
- 癒着していないアプリケーションは、関心(concern)が明確に分離されて問題の特定や修正も容易になるので、メンテナンスしやすくなります。
今後開催予定のRailsビジネスロジックマスターコースでも同様の問題を取り上げる予定です。お見逃しなきよう、元記事末尾のフォームでぜひニュースレターを購読してください。
概要
元サイトの許諾を得て翻訳・公開いたします。