rails-erdを使って分割したyamlの設定ファイルごとにERDを自動生成する

## はじめに

前回の「[rails-erd はどうやってエンティティや関連づけ情報を取得しているのか](https://qiita.com/akitoshi-n/items/e4fdc7746bbff329776b)」に引き続き、rails-erd 関連の記事になります。

[rails-erd](https://github.com/voormedia/rails-erd) を使うと ERD をコマンド一つで自動生成できます。ただ、一つの ERD に全てのモデルを表示させようとすると、 <b>かなり見辛い</b> です。そこで今回は、グループ分けしたモデルごとに yaml の設定ファイルを用意し、コマンドで一つで、全てのグループの ERD を一括生成する実装について紹介したいと思います。

ちなみに、エンブレースが開発している [MCS](https://www.medical-care.net/html/) の全モデルを1つの ERD に含めて生成するとこんな感じになります😭
（※ 解像度を低くしてます。）

![Screen Shot 2020-10-26 at 12.56.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/259456/7b9b4cae-6b55-9e2c-88c1-4f3b935948de.png)


## 全体像

まずは、出来上がるファイル構成を紹介します。

```
erd
 ├── config
 │    └── xxx.yml     # 設定ファイルたち
 ├── xxx.png          # 生成されたERDたち
lib
 └── task/erd.rake    # タスク
```

- erd/config/ 配下に xxx.yml という設定ファイルを設置
- lib/task/erd.rake にタスクを作成
- erd.rake で作成したタスクを実行すると、erd/ 配下に ERD が自動生成される🎉

といった感じのものを作っていきます。

## rails-erd の導入

rails-erd の導入方法については、他に紹介している記事がたくさんあるので、こちらの記事では省略します。

## 設定ファイルの設置

ファイルごとに全てのオプションを書くのは冗長なので、共通オプションを記載する共通ファイルを一つ用意し、個別ファイルには共通ファイルとの差分のみ記載するような作りにします。

### 共通ファイル

まずは、共通ファイルを準備し、オプションを記載します。

```yaml:common.yml
attributes:
  - content
  - foreign_key
disconnected: true
filename: erd/dummy
filetype: png
indirect: false
inheritance: true
markup: true
notation: crowsfoot
orientation: vertical
polymorphism: false
sort: false
warn: false
title: dummy
exclude: null
only: null
only_recursion_depth: null
prepend_primary: false
cluster: false
splines: spline
```

※ ちなみに、どのようなオプションがあるかは、以下のコマンドで確かめられます。

```bash
$ erd -h
```

### 個別ファイル

次に、個別ファイルを作成します。
こちらは、`title` と `only` だけの指定になるので、かなり簡潔です😊

```yaml:xxx.yml
title: sample title
only:
  - XXX
  - YYY
  - ZZZ
```

※ 個別ファイルのファイル名と生成される ERD のファイル名が異なっているとわかりづらいので、オプションの filename は、タスクの中で自動的に個別ファイルのファイル名で上書きされるようにします。

## タスクの作成

`Kernel.#system` を使えば、`system("bundle", "exec", "erd")` のような形で、rails-erd が用意したタスクを実行できますが、より簡潔にするため RailsERD::CLI を使って実装していきたいと思います。
※ RailsERD::CLI は、名前の通り ERD の生成を CLI から実行するためのクラスです。

```ruby:erd.rake
# RailsERD::CLI を利用できるようにするため、rails_erd/cli を読み込む
require "rails_erd/cli"

namespace :erd do
  desc "ERDの一括生成"
  task bulk_generate: :environment do
    common_file = Pathname.new("erd/config/common.yml")
    common_options = YAML.load_file(common_file)

    partial_files = Pathname.new("erd/config").glob("*").reject {|file| file == common_file }
    partial_files.each do |partial_file|
      partial_options = YAML.load_file(partial_file)
      partial_file_name = File.basename(partial_file, ".*")

      # 共通オプションと個別オプションの結合
      # 個別ファイルのファイル名で filename を上書き
      # キーを文字列からシンボルに変更する
      options = common_options
                  .merge(partial_options)
                  .merge({ filename: "erd/#{partial_file_name}" })
                  .symbolize_keys

      # ERD生成の開始
      RailsERD::CLI.new(Dir.pwd, options).start
    end
  end
end
```

これで、以下のコマンドで ERD が一括生成されるようになるはずです！

```bash
$ bundle exec rake erd:bulk_generate
```

## モデルのグループ分けのコツ

上記で ERD の一括生成の方法の説明は終わりなのですが、モデルのグループ分けも一筋縄ではいかなかったので、筆者がグループ分けしてみて得た知見を共有したいと思います。

### 機能ごとにグループ分けする

- 例）ログイン機能、通知設定機能、etc.
- メリット
  - 機能ごとに分けることでビジネスロジックが理解しやすくなる
  - テーブル数を程よく少なくできる

### 1つのERDに含むテーブルはせいぜい10個程度にする

一つの ERD に10個以上のテーブルが入る場合は、機能を分割できないか検討してみると良いです。

- メリット
  - どのテーブル、関連付けが重要かすぐにわかる
  - 見辛さを緩和できる

### ファイル名は、機能の中心となるテーブル名に寄せる
- 例）通知設定のメインのテーブルが `notification_settings` であれば、ファイル名を `notification_setting.yml` にする
- メリット
  - ファイル名とテーブル名が結びつき、何の機能か推測しやすい
  - 中心となるテーブルがわかる
  - ファイル名に統一感を出すことができる
- 補足
  - 機能の中心となるテーブルが中間テーブルで、機能の名称として相応しくない場合や、すでに機能に名前がついている場合などは、テーブル名とは別の名前をつけるのが吉

## 最後に

rails-erd を使用して複数の ERD を一括生成する方法、そしてモデルのグループ分けのコツについて紹介しました。新規メンバーとしてプロジェクトに加わった際には、ERD があるとビジネスロジックの理解がスムーズになるため、大変ありがたいです。こちらの記事が ERD を用意する上での参考になれば幸いです。

## 参考
- [rails-erd - Github](https://github.com/voormedia/rails-erd)