RailsアプリにSchemaSpyを導入してER図を自動生成してみた（MySQL 8.0）

# はじめに
メタップスアドベントカレンダー第23日目です。

開発をしているとドキュメントを見ている時に 「あれ? 仕様と実装がちょっと違う??」 「このドキュメントが最後に更新されたのいつだろ??」 という状況に陥った経験がある方はいらっしゃるのではないでしょうか。

私は過去にRDBのリレーションを視覚的にパッと把握するためのドキュメントが見つけられない経験がありました。そこで、今回は**SchemaSpyを使ってER図を自動生成する方法**を試してみました。

## SchemaSpyとは
データベースの情報を元に、ER図、テーブル、カラムなどの情報をHTML形式のドキュメントとして出力するツールです。

# 検証
Railsアプリ、MySQL、SchemaSpyをコンテナ上で動かし、MySQLの情報をSchemaSpyで読み込み、ER図を作成していきます。SchemaSpyには [docker image](https://hub.docker.com/r/schemaspy/schemaspy/tags)を使用します。最終的なディレクトリ構成はこのようになります。

```
project
  ├ app
  ├ db
  ├ schemaspy
  │  ├ output    # 自動生成されたものが出力される箇所
  │  ├ DockerFile
  │  └ schemaspy.properties
  ├ DockerFile
  └ docker-compose.yml
```

### 環境
```
- Rails 7.0.4
- ruby 3.1.2
- MySQL 8.0
- schemaspy 6.1.1-SNAPSHOT
```

## 検証用アプリの作成
まずは簡単なRailsアプリを作成していきます。
プロジェクト配下に`DockerFile`と`docker-compose.yml`を作成します。

```dockerfile:project/DockerFile
FROM ruby:3.1.2

RUN apt-get update -qq && apt-get install -y nodejs yarnpkg
RUN ln -s /usr/bin/yarnpkg /usr/bin/yarn
RUN mkdir /app
WORKDIR /app
COPY Gemfile /app/Gemfile
COPY Gemfile.lock /app/Gemfile.lock
RUN bundle install
COPY . /app
COPY entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
ENTRYPOINT ["entrypoint.sh"]
EXPOSE 3000

# Start the main process.
CMD ["rails", "server", "-b", "0.0.0.0"]
```

```yml:project/docker-compose.yml
version: '3'
services:
  mysql:
    platform: linux/x86_64
    image: mysql:8.0
    volumes:
      - ./tmp/db:/var/lib/mysql
    environment:
      MYSQL_DATABASE: app_development
      MYSQL_USER: root
      MYSQL_ROOT_PASSWORD: pass
      TZ: 'Asia/Tokyo'
    ports:
      - "3306:3306"
  web:
    build: .
    command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
    volumes:
      - .:/app
    ports:
      - "3000:3000"
    depends_on:
      - mysql
```

`DockerFile`と`docker-compose.yml`の準備ができたら`rails new`コマンドを実行してアプリを作成します。
```
docker-compose run --no-deps web rails new . --force --database=mysql --skip-bundle
```

`project/app/config/database.yml`を修正してコンテナで立ち上げたMySQLのユーザ、パスワード情報を設定します。

```yml:database.yml
default: &default
  adapter: mysql2
  encoding: utf8mb4
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: root
  password: pass
  host: mysql

...
```

migrationファイルを作成して、ER図で出力するテーブルを作成していきます。
今回はSchemaSpyでER図を出力することが目的なので、ここでのカラム情報や型はよしなに作っていきます。


```rb:20221205093018_create_products.rb
class CreateProducts < ActiveRecord::Migration[7.0]
  def change
    create_table :products do |t|
      t.references :supplier, foreign_key: true, null: false
      t.string :name
      t.string :category

      t.timestamps
    end
  end
end
```

```rb:20221205002147_create_suppliers.rb
class CreateSuppliers < ActiveRecord::Migration[7.0]
  def change
    create_table :suppliers do |t|
      t.string :name
      t.integer :phone_number
      t.string :postal_code
      t.string :address

      t.timestamps
    end
  end
end
```

```rb:20221205100523_create_customers.rb
class CreateCustomers < ActiveRecord::Migration[7.0]
  def change
    create_table :customers do |t|
      t.string :first_name
      t.string :last_name
      t.string :email
      t.integer :phone_number
      t.string :postal_code
      t.string :address

      t.timestamps
    end
  end
end
```

```rb:20221205100739_create_orders.rb
class CreateOrders < ActiveRecord::Migration[7.0]
  def change
    create_table :orders do |t|
      t.references :customer, foreign_key: true, null: false
      t.string :order_date
      t.string :preferred_delivery_date
      t.string :channel
      t.string :address
      t.integer :total_amount

      t.timestamps
    end
  end
end
```

```rb:20221205100903_create_order_details.rb
class CreateOrderDetails < ActiveRecord::Migration[7.0]
  def change
    create_table :order_details do |t|
      t.references :product, foreign_key: true, null: false
      t.references :order, foreign_key: true, null: false
      t.integer :quantity
      t.integer :price

      t.timestamps
    end
  end
end

```

マイグレーションファイルの準備ができたら、コマンドを実行してテーブルを作成していきます。

```
docker-compose run web rails db:create
```

## SchemaSpyの設定

検証用アプリの準備ができたので、次はSchemaSpyの設定です。`project/schemaspy`のディレクトリを作り、`DockerFile`と`schemaspy.properties`を作成していきます。


```dockerfile:schemaspy/DockerFile
FROM --platform=linux/amd64 schemaspy/schemaspy:snapshot

ENV TZ=Asia/Tokyo
USER root
COPY schemaspy.properties ./schemaspy
```

検証用アプリで使用している設定値を記載していきます。

```schemaspy/schemaspy.properties
schemaspy.t=mysql
schemaspy.dp=/drivers
schemaspy.host=mysql
schemaspy.port=3306
schemaspy.db=app_development
schemaspy.s=app_development
schemaspy.u=root
schemaspy.p=pass
```

`project/docker-compose.yaml`に`schemaspy`を追加して、使用するdocker image、自動生成されるファイルの出力先、`schemaspy.properties`のパスなどを定義していきます。


```yml:project/docker-compose.yml
  schemaspy:
    container_name: schemaspy
    image: schemaspy/schemaspy:snapshot
    volumes:
      - ./schemaspy/schemaspy.properties:/schemaspy.properties
      - ./schemaspy/output:/output
    depends_on:
      - mysql
    environment:
      SCHEMASPY_OUTPUT: /output 
```

ここまで準備ができたらあとはコマンド実行するだけです！
### いざ起動！！！
```
docker-compose up
```


このようなログが出れば成功です！
![スクリーンショット 2022-12-18 18.57.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/185271/69678a9d-3f92-307f-e3c3-e8c2d0092115.png)


`/schemaspy/output `に出力された`index.html`をブラウザでOpenするとテーブルの概要ページを確認することができます。 カラフルでいい感じですね！
![スクリーンショット 2022-12-18 19.21.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/185271/982e9456-3af1-471d-56da-d3d88878274c.png)

テーブルを選択すると、そのカラムの詳細情報や関連のあるテーブルたちを確認できます。試しに`products`テーブルを選択してみるとこんな感じです。右上のアイコンから、CSV、Excel、PDF形式でテーブルをExportできるのも色々便利そうです。

![スクリーンショット 2022-12-18 19.40.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/185271/dfde18c2-0cd1-94ce-e66e-163841ea67ba.png)


開発が進んでテーブル数が多くなってきた場合、全体のリレーションを表示すると情報量が多すぎて視認性が下がりそうなので、選択したテーブルが関係するリレーションのみを見れるのも便利ですね。

![スクリーンショット 2022-12-18 19.45.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/185271/11175c42-d361-e7da-0d8b-76cc30b4e6f2.png)

トップ画面で`Relationships`項目を選択すると、データベース内に存在するすべてのテーブルのリレーションも閲覧できます。

![スクリーンショット 2022-12-18 19.28.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/185271/928b84ff-cab5-0601-063b-519ec0653e8f.png)

## ちょっと詰まったところ
基本的には簡単にER図を出力することができたのですが、SchemaSpyの出力時に発生したエラー紹介です。

```
ERROR - Bad config
Schema (-s/-schemas) was not provided and unable to deduce schema, schema is sometimes referred to as user/owner/database
```
`sechemaspy.properties`でスキーマ名を指定していなかったことが原因だったので、以下の1行を追加して解消。

```
schemaspy.s=app_development
```


## おわりに
開発初期にドキュメントを作成したけれど、日が経つにつれて最新版に更新されずにそのままになっている。。というのは陥りがちな光景だと思います。今回は、手元でSchemaSpyを実行してER図を自動生成する方法を試しましたが、Github Actions + S3 を使って、データベースに変更が加わったら最新のER図が自動生成される仕組みも、今後試してみたいなと思います。