くりにっき

フルスタックキュアエンジニアです

ERDをPlantUML形式で自動生成するツールを作った

PlantUML + ERDでPlantERDです

github.com

モチベーション

既存プロダクトへの不満が一番大きいです。

  • https://github.com/voormedia/rails-erd は出力が画像なので取り回ししづらい
    • そもそもRails前提なので他言語とかでは使えない
  • https://github.com/schemaspy/schemaspy も悪くなさそうなんだけどここまでリッチじゃなくていい
  • テーブル数個の小規模アプリならいいんだけど、中規模以上のアプリで使うと人間が読むに耐えないERDが生成されて精神が崩壊する
    • 僕は初めて触るアプリだと実装を把握するために特定のテーブルに隣接するテーブルのみ抽出して関係性を抽出してるんだけど、そういうことを自動でやりたかった

PlantERDの特徴

  • golangで作ったシングルバイナリなのでバイナリポン置きでどこでも使える
  • 出力形式がPlantUML(プレーンテキスト)なので取り回ししやすい
    • plant_erdで生成したERDをコミットすることでスキーマ変更前後の差分もgit上でいい感じに管理できると思います
    • UMLの出力だけ行うことで、テーブル配置の座標計算や画像生成などをツール内で考えなくてよかったのが嬉しい
  • 対応してるDBは下記
  • 「usersテーブルからForeign Key 2本以内の距離のテーブルまでを出力」のように、出力するテーブルを指定できる。(後述)

使い方

SQLite3だとこんな感じ。MySQLPostgreSQLもオプションが増えるだけで使い方はだいたい一緒です

$ ./plant_erd sqlite3 --database /path/to/test_db.sqlite3

entity articles {
  * id : integer
  --
  * user_id : integer
  --
  index_user_id_on_articles (user_id)
}

entity users {
  * id : integer
  --
  name : text
}

articles }-- users

f:id:sue445:20191212225017p:plain

出力するテーブル数の制限について

例えばこういう7つのテーブルがあるとしてます。

$ ./plant_erd sqlite3

f:id:sue445:20191212230004p:plain

これを

$ ./plant_erd sqlite3 --table articles --distance 1

のようにオプションを渡すことで、articlesテーブルからForeign Keyで隣接する距離1以内のテーブル(つまりarticlesテーブルと直接隣接するテーブル)のみ出力することができます。

f:id:sue445:20191212230239p:plain

技術的に頑張ったこと

テストのこと

ツールの性質上MySQLPostgreSQLといった外部のミドルウェアがないとテストができないという問題があります。

ローカル環境を汚したくなかったので このようなdocker-compose.yml を書いて docker-compose up するだけでローカルでMySQLPostgreSQLを使ったテストができるようにしています。(MySQLPostgreSQL環境変数がセットされていなければスキップしてSQLite3のテストしかしない)

あとマトリクステストを頑張ったのでDockerHubにあったMySQLPostgreSQLのイメージの主要バージョンは全部テストしています。

Foreign keyで隣接している別のテーブルを探す方法

大学の卒論でグラフ理論をやっていたのでその時の経験を生かしてgolangで無向グラフを実装しています。*1

詳しくはこの辺のソースを見てください。(途中まで行列を書いて数学的に説明を書こうとしてたけどブログウケしなさそうなのでやめた)

複数DB対応のつらみ

SQLDDLDMLなどはSQL99で仕様化されているので多少方言はあるものの各DB間でそんなに差異はありません。

しかしplant_umlで必要なテーブル情報やインデックス情報の取得方法は各DBの実装に大きく依存していたので実装するのはかなり苦労しました。

この辺に僕の苦労が詰まっています。

この辺の実装はactiverecordのconnenction_adaptersの実装をむっちゃ参考にさせてもらいました。

https://github.com/rails/rails/tree/v6.0.1/activerecord/lib/active_record/connection_adapters

追記:2019/12/13 9:45

id:tinsep19

schemespyでテーブルのリンク辿ると、距離1,2の関連図があるけどね

重箱の隅をつつくような細かい指摘ありがとうございます。

上にも書いているようにここまでリッチじゃなくていい(cliで完結したい)ってのと、gitでコミットした時に履歴が読める形式のdiffがほしいという理由でUMLで出力したいという気持ちがありました。(schemespyはサンプル見た感じ画像かsvgでしか出力してなかった。もし認識違ってたらまた重箱の隅をつつきにきてください)

あとPlantUMLは距離1,2に関わらず任意の距離の関連図を取れるのでそこだけは優位性がありますね。

*1:余談ですが大学の卒論ではC++で無向グラフを実装しました