TL;DR
データベースアクセス層はapp/models/repoに書くことにした。
追加のgemとか不要でめでたしめでたし。
※リポジトリパターンとか言ってるけどDDDはよく知らない ![]()
動機
Railsを使っているとFat Modelになりがちですね。
解決策としてサービスオブジェクト作ったりしますが、純粋なActiveRecordとその他のビジネスロジックをapp/modelsにおいてしまうのか、app/logicsとかapp/servicesとか作るのかとか色々迷うなーって部分があると思います(僕はあります)。
最近Growing Rails Application in Placticeという本を読んで、やっぱりapp/modelsの下に置いた方が良い気がしました。
純粋なActiveRecordじゃなくてもActiveModelとか使って、バリデーションやコールバック、エラー処理などの恩恵を受けた方がRailsWayっぽいし、それならapp/modelsに入るだろうなーという感じです。
となると問題になるのがapp/modes以下の構成。
実際にDBのテーブルにアクセスするモデルと、それらを利用してビジネスロジックを実装するクラスが同一階層に並んでると見通しが悪いですし、気持ち悪いです。
個人的にはtreeしてディレクトリ構成を見れば中身の予想まである程度つく状態が良いです。
対応
今のことろの自分は以下のような構成にしてます。
$ tree app/models
app/models/
├── concerns
├── post.rb /* app/models/repo 以外はビジネスロジック */
├── post
│ ├── post_form.rb
│ └── post_search.rb
├── signin.rb
├── signup.rb
├── repo /* app/models/repo 以下に純粋なActiveRecord */
│ ├── category.rb
│ ├── post.rb
│ └── user.rb
└── repo.rb
考え方
考え方としては、基本モデルはビジネスロジックでしょ!という感じで、データベースアクセス層(純粋なActiveRecordのクラス)はapp/models/repoディレクトリ以下に押しこめます。
そしてapp/models直下およびrepo以外のサブディレクトリにデータベースアクセス層を利用する(または利用しない)ビジネスロジックを記述していきます。
ビジネスロジックのクラスは、必要に応じてRepo::以下のクラスを継承するか、委譲してコードを書いています。本来は継承しているクラスも別の層に分割した方が綺麗だと思うのですがその辺は妥協しています。
例えばSignupクラスはRepo::Userを継承して実際にレコードを作成しますが、Signinクラスは与えられたユーザーIDとパスワードからRepo::Userを検索して認証を行います。
この際にSignupも委譲での実装にしてしまうこともできますが、継承の方がモデルのバリデーションをそのまま使えてViewのフォームなどが楽に書けたりするので。
ビジネスロジック層のさらなるディレクトリ分けはまだ迷っていて、アプリケーションによってはコントローラごとに分けた方が便利なこともあるかもな、、と思ってます。
Tips
ちなみに普通にデータベースアクセス層のモデルを作成するときはmigrationも作りたいので rails gしたいのですが、普通に
bin/rails g model Repo::User email:string password_digest:string
とかやると
$ tree app/models
app/models/
├── repo
│ └── user.rb
└── repo.rb
とできるのは良いのですが、migrationファイルに
create_table :repo_users do
・・・
end
と書かれててしまい、このままではテーブル名に実装の都合が反映されてしまいます。別にこのままでも実装続けられますが、ちょっと気持ち悪いので
create_table :users do
・・・
end
に直した上で、app/models/repo.rbを
module Repo
def self.table_name_prefix
'repo_'
end
end
から
module Repo
# ここではから文字にしているが、このメソッド定義を削除でも良い。
# 今のところは明示的にこうしてるんだよって意味合いで残している。
def self.table_name_prefix
''
end
end
に変更します。
こうすることでテーブル名に余計な接頭辞がつかなくなり、ActiveRecordクラスも個別に self.table_nameを指定しなくて済みます。
最初このディレクトリ構成にしようと思ったときに「毎回table_name設定するのやだなー」とか思ってたんですが、案外すんなり解決しました。
コード書いていく上でベースになる部分なので、こういう部分こそ追加のgemなしでできるのはありがたいですね。
ついで
Growing Rails Application in Placticeとても良い本でした。
英語だけど100ページ未満くらい(MBPのiBooksで読んだ場合)だし平易だと思うのでお薦めです。
これ読んでActiveTypeっていうgemが手放せなくなった。。