PlantUML + ERDでPlantERDです
モチベーション
既存プロダクトへの不満が一番大きいです。
- https://github.com/voormedia/rails-erd は出力が画像なので取り回ししづらい
- そもそもRails前提なので他言語とかでは使えない
- https://github.com/schemaspy/schemaspy も悪くなさそうなんだけどここまでリッチじゃなくていい
- テーブル数個の小規模アプリならいいんだけど、中規模以上のアプリで使うと人間が読むに耐えないERDが生成されて精神が崩壊する
- 僕は初めて触るアプリだと実装を把握するために特定のテーブルに隣接するテーブルのみ抽出して関係性を抽出してるんだけど、そういうことを自動でやりたかった
PlantERDの特徴
- golangで作ったシングルバイナリなのでバイナリポン置きでどこでも使える
- 出力形式がPlantUML(プレーンテキスト)なので取り回ししやすい
- 対応してるDBは下記
- SQLite3
- MySQL: 5.6, 5.7, 8
- PostgreSQL: 9, 10, 11, 12
- 「usersテーブルからForeign Key 2本以内の距離のテーブルまでを出力」のように、出力するテーブルを指定できる。(後述)
使い方
SQLite3だとこんな感じ。MySQLやPostgreSQLもオプションが増えるだけで使い方はだいたい一緒です
$ ./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
出力するテーブル数の制限について
例えばこういう7つのテーブルがあるとしてます。
$ ./plant_erd sqlite3
これを
$ ./plant_erd sqlite3 --table articles --distance 1
のようにオプションを渡すことで、articlesテーブルからForeign Keyで隣接する距離1以内のテーブル(つまりarticlesテーブルと直接隣接するテーブル)のみ出力することができます。
技術的に頑張ったこと
テストのこと
ツールの性質上MySQLやPostgreSQLといった外部のミドルウェアがないとテストができないという問題があります。
ローカル環境を汚したくなかったので このようなdocker-compose.yml を書いて docker-compose up
するだけでローカルでMySQLとPostgreSQLを使ったテストができるようにしています。(MySQLやPostgreSQLの環境変数がセットされていなければスキップしてSQLite3のテストしかしない)
あとマトリクステストを頑張ったのでDockerHubにあったMySQLとPostgreSQLのイメージの主要バージョンは全部テストしています。
- https://github.com/sue445/plant_erd/blob/v0.1.0/.github/workflows/test.yml#L19-L23
- https://github.com/sue445/plant_erd/blob/v0.1.0/.github/workflows/test.yml#L94-L99
Foreign keyで隣接している別のテーブルを探す方法
大学の卒論でグラフ理論をやっていたのでその時の経験を生かしてgolangで無向グラフを実装しています。*1
詳しくはこの辺のソースを見てください。(途中まで行列を書いて数学的に説明を書こうとしてたけどブログウケしなさそうなのでやめた)
- https://github.com/sue445/plant_erd/blob/v0.1.0/db/schema_explorer.go
- https://github.com/sue445/plant_erd/blob/v0.1.0/db/undirected_graph.go
複数DB対応のつらみ
SQLのDDLやDMLなどはSQL99で仕様化されているので多少方言はあるものの各DB間でそんなに差異はありません。
しかしplant_umlで必要なテーブル情報やインデックス情報の取得方法は各DBの実装に大きく依存していたので実装するのはかなり苦労しました。
この辺に僕の苦労が詰まっています。
- https://github.com/sue445/plant_erd/blob/v0.1.0/adapter/mysql/adapter.go
- https://github.com/sue445/plant_erd/blob/v0.1.0/adapter/postgresql/adapter.go
- https://github.com/sue445/plant_erd/blob/v0.1.0/adapter/sqlite3/adapter.go
この辺の実装はactiverecordのconnenction_adaptersの実装をむっちゃ参考にさせてもらいました。
https://github.com/rails/rails/tree/v6.0.1/activerecord/lib/active_record/connection_adapters
追記:2019/12/13 9:45
schemespyでテーブルのリンク辿ると、距離1,2の関連図があるけどね
重箱の隅をつつくような細かい指摘ありがとうございます。
上にも書いているようにここまでリッチじゃなくていい(cliで完結したい)ってのと、gitでコミットした時に履歴が読める形式のdiffがほしいという理由でUMLで出力したいという気持ちがありました。(schemespyはサンプル見た感じ画像かsvgでしか出力してなかった。もし認識違ってたらまた重箱の隅をつつきにきてください)
あとPlantUMLは距離1,2に関わらず任意の距離の関連図を取れるのでそこだけは優位性がありますね。