見出し画像

さようならActiveRecord

こちらの訳になります。

ActiveRecordに代わるORマッパーとして提唱されてているROMに関するお話です。リポジトリパターンとかバリューオブジェクトとかその辺のテクニックをいろいろ詰め込んでファットモデルをバラバラにしようみたいな考えみたいです。試してみる価値はありそう。

以下訳。


AirCallでは、エンジニアたちがごちゃごちゃしたRailsのレガシーなモノリスと格闘している。いま徐々に軽量なRuby×Sinatra製のマイクロサービスに分割を進めているところだ。また、過去の失敗を繰り返さないために、クリーンアーキテクチャやドメイン駆動開発といった新しいアーキテクチャを試し始めた。そしてROM(Ruby Object Mapper)はこれらの思想によくマッチしたものだった。

ROMがいかに便利なものかをよく知ってもらうために、まずはActiveRecordがもたらす問題点について見てみよう。それからROMがどんなものか、そのコンセプトについて説明し、最後にこのツールの注目すべき点について解説する。

ActiveRecordの限界

関心の分離をうまく行おうとするとき、まずわれわれはビジネスロジックをデザインしようとするはずだ。しかし、どのデータベースを用いるか、どう効率的にデータを永続化するかなどといったことには頭を悩ませたくない。アプリケーションをデザインするとき、最初から全てのユースケースが明らかなことはまずない。しかし、どのようなデータストアが最も効率的かは、ユースケースによって決まる。

さくっとアプリケーションを作りたいとき、ActiveRecordは簡単で便利な選択肢だ。疎結合で高凝集な永続化レイヤーをどう開発したらいいかということに悩まされることなく、全ては内部で魔法のように処理してくれる。しかしこの手っ取り早さは、スケール(アプリケーションやチームの拡大、CRUD以上のことをしようとするとき...)を考えたときに悪夢になる。というのも、他の技術で埋め合わせをして結局は永続化レイヤーを複雑化させることになるからだ。同時にこのとき、モデルがファットで、複数のことをやっていることに気づく。大抵の場合、リファクタリングのコストが高くつくため、関心の分離に従って分割するにはすでに手遅れになっているころだろう。

私にとってActiveRecordの一番の問題点は、同時にそれは強みでもあるのだが、あまりに多くのものを 「モデル」と称してしまっていることである。

• データの書き込み(クエリは徐々に複雑化していく)
• 任意のデータの取得(複雑な条件で)
• 永続化されている属性に基づく新たな属性の作成

これらのことを試みるとき、高い確率で全てはモデルに押し込められることになるはずだ。ここにROMの利点がある。ROMは、それぞれに別の名前をつけて区別している。

ROMのコンセプト

ROMについて詳しく知りたい場合はこちら
この章では、ROMの核となる考え方と、それらがどのように協調するかを見ていく。以下の図にいくつかの概念を示す。

公式のスキーマと概念の定義

Business Layer とはビジネスロジックを扱うあらゆるコードを指す。
コントローラ、ワーカー、スクリプト、CLIもそうだ。

• 永続化レイヤーについては何も知らないべきである。どのデータストアを使っているか、どのようにデータが保存され、関連づけられ、読み書きされるか、など。
Repositories を介してのみデータの取得/操作を行うべきである。
Repositories から戻される ROM::Structs を用いてローデータを公開するのがデフォルト。モンキーパッチなしではビジネスロジックに関係するメソッドを追加できないので、別で Business Entity を作ることになる。ここがそれらのメソッドの置き場所となる。

・・・

Entities Business Layer のためににうまくデータを取り扱ってくれる。

• ミュータブルだが、直接データストアには接続しない。
• ActiveRecord のようにデータの書き込みをトリガーすることはできない。(書き込みは Repositiories を用いて行う必要がある。さらに Repositories は Commands を介して書き込みを行う)
• いかなる読み込みもトリガーすることはできない。(関連レコードを取得するような)
• データストアに保存されている形式とは別のインターフェースを提供することもできる。(例えば、MySQLでは文字列化されたJSONをカラムに保存できるが、Entity においては通常のHashとして扱うことができる)
• 複数の関連レコードの集合体にもなり得る。
• ビジネスロジックのために便利なメソッドを提供する。(first_name, last_name の2つのカラムを結合した full_name という属性のような)

Entities は、開発者が使いやすい形でビジネスオブジェクトを操作することに主眼を置いている。データストアに接続していないので、読み書きを行うことはできない。(コールバックや遅延ロードが行うような)

・・・

Repositories は、Business Layer と永続化レイヤーの中間インターフェースである。

•  一般的に、1つの Repository は1つの Entity を扱う。
•  最低1つの Relation を持つ。(例えば、 Repositories::Users のインスタンスは Relation::Users のインスタンスを持っている。これは、users といいうビルトインメソッドを介してアクセスできる)
•  データストアについてはの情報は持っていない。(外部サービスからでもファイルストレージからでも、 同じ方法でデータを取得することができる)
•  Relation のメソッドを使って、データのフィルタリング・関連づけ・集計を行うが、内部の動作については関与しない。(フィルタリングが where を使って行われるか別の方法かといったような)
•  オブジェクトの関連づけについて、X Y を関連づけるべきといったことは知っているが、外部キーを使うかテーブルジョインを使うかといった方法については知らない。
•   デフォルトで ROM::Structs を返すが、独自の Entity を定義してBusiness Layer で使用している場合は、合致する Entity に変換する責務を担う。

・・・

Relations は、データの部分的なビューのようなものである。SQLのテーブルであったり、HTTPバックエンドにおけるCRUDリソースだったり、ファイルストレージシステム上のJSONファイルだったりする。

•  データストア(正確に言えばデータセット)特有のメソッドを他から隠蔽し、より抽象化された形で提供する。(SQLであれば where, select のような)
•  定義または推論されたテーブルスキーマをもつ。スキーマはデータストアに存在する属性と関連づけを含む。
•  データの読み出しのみに利用される。

Relations を適切に使用すれば、SQL関数のようなデータストア特有のメソッドを RepositoriesBusiness Layer で目にすることはなくなる。データストアを変更したい時でも、 Relations の実装を変えるだけでよいので簡単である。

・・・

Commands はデータの変更に用いられる。変更を適用するために Relations に依存している。

Datasets はデータとデータストアのメソッドを実装した基本的なオブジェクトで、Relations から操作される。

Datastore はストレージシステムで、SQLデータベース、HTTPバックエンド、ただのファイルシステムなど様々である。

これらの概念はすべて一貫しており、慣れてしまえばどこに何を追加すればいいかとてもわかりやすい。

ROMを学ぶ

もしあなたが熟練のRuby開発者であれば、ROMを学習することは大きな救済となるかもしれない。ROMは一般的なORマッパーが抱える問題を解決し、あなたのコードを整理する別の方法を提示してくれるだろう。逆にジュニアな開発者にとっては痛みとなるかもしれない。以下に理由を示す。

•  コミュニティがとても小さくなっている。SatckOverflowはなく、本当に他の人の助けが必要な場合は、フォーラムチャットが唯一の頼りとなる。
•  ドキュメントはSpecとコードの中だ。インターネットで多くの例を見つけられる時代にこれは異常なことである。適切に使おうとすれば時にはコードの内部を読まなければならず、多くの時間と労力を必要とする。ジュニアであればなおさらだ。

さて、我々がROMを導入するのは難しかっただろうか?イエス。今現在ROMを使っていることを後悔しているだろうか?全く!むしろROMのおかげで、コードが以前よりとてもきれいになったと思っている。また、アーキテクチャに関して考えるきっかけを与えてくれた。(クリーンアーキテクチャやヘキサゴナルアーキテクチャのその先へ)

ROMを学びたい人には、ここから始めてみることをオススメする。

もっと具体例を見たい場合はこのスライドを見てみるとよい。

このテーマについて話したければRedditのスレッドに参加してください。


以上です。ご査収ください。


この記事が気に入ったら、サポートをしてみませんか?気軽にクリエイターを支援できます。

note.user.nickname || note.user.urlname

いただいたサポートでお菓子を買います 🍰

ありがとうございます〜 🦐
13

sakuraya

株式会社ITMONOで執行役員CTOをしています。 お買い物アプリを作っています。(https://apple.co/2yMy6PL

エンジニア向け記事翻訳

1つ のマガジンに含まれています
コメントを投稿するには、 ログイン または 会員登録 をする必要があります。