作図の手間から開放する、ダイナミックER図の試作してみた
開発チームの下田です。
同僚と雑談でER図について話してました。
新しいメンバーを迎えるときにはER図があると便利だけど、100テーブルを超えたあたりから線が絡まりすぎて無理だよねー
おそらく業務システム開発にかかわる人なら誰しもが似たような経験があるのではないでしょうか。
次のようなことができたらいいなあと、DDLからER図を生成するSPAを試作してみました。
- DDLから動的にER図を作図してほしい
- 動的に位置関係を調整してほしい
- 動的に注目しているエンティティとリレーション、もう一つ先のエンティティを強調して見やすくしたい
試作のため、バグはかなり残っていると思いますがご容赦ください。
作ったもの
インタラクティブに図を読む体験ができるダイナミックER図、Force Directed ER Diagramを作ってみました。
動的にDDLをパースしてER図を描画して、動的に力学モデルで線の調整します。
https://onfi.github.io/force-directed-er-diagram/
動かすには、mysqldumpで出力したDDLをtextareaに貼り付け、RENDERボタンをクリックします。
mysqldump -d [database-name]
ベストエフォートで線をすっきり表示します。
気に入らなければドラッグ&ドロップすると、動的に再調整します。
テーブルをクリックすると、リレーションが強調されて、関係テーブルのカラムの一覧を表示します。
ソースはこちら
https://github.com/onfi/force-directed-er-diagram
仕様検討
ここからは試作にあたって考えたことのメモです。
着想
グラフ視覚化の手法を応用すれば、解決できると考えました。
ER図はグラフです。視覚化するという意味のグラフでも、グラフ理論的にもグラフです。ややこしいので、この記事では視覚化するほうを「ダイアグラム」、グラフ理論のほうを「グラフ」と呼びます。
グラフを描画してダイアグラムにするライブラリはたくさんあり、きれいに描画してくれます。グラフデータベースライブラリ neo4jのグラフ描画ツールの解説にまとまっています。
どうやら 力学モデルグラフ が、どんなにテーブル数が増えても良い感じに自動で調整してくれそうです。
ユースケース
RailsでWEBアプリ開発を行い、開発環境ではdockerでDBMSもセットで立ち上がる場合を想定しています。
- 開発者ごとにDBがあるので、上流工程でDB設計が確定している必要性がない
- アプリにあわせてDB設計するので、DB設計より先にDDLができる場合がある
このような特徴があるため、アプリ側での仕様変更をER図にリバースする必要がありますが、前述した線を調整するのが大変問題で工数がかかります。アジャイルソフトウェア開発宣言に従う場合は特に相性が悪いです。
ER図は作らないか、誰も読み取れない形式的なものを作ってしまうことが多いように思います。
- 動くソフトウェアにフォーカスし、ドキュメンテーションに工数をかけたくない
- 既存のDB関連のドキュメントは無い・もしくは破綻している
- 必要なときに図解して理解を助ける
- オンボーディングで説明するときや、設計で迷ったときなど
こんなシーンで使えるER図を目指します。
要件定義
たとえ試作であっても、必ず実装する要件と、余裕がなければ諦めるフィーチャーバッファとして仕様を分けて考えています。
必ず実装する
- 力学モデルで動的に調整する
- エンティティ名とリレーションを表示する
- mysqldumpで生成したDDLをパースする
- FOREIGN KEYがある場合、リレーションとする
- ActiveRecordパターンの命名規則に従っている場合
- インタラクティブな操作性にする
- 真面目に考えると要らないが、今回は遊びで試作しているので「なんとなく楽しそう」が優先度高
フィーチャーバッファ
- カラムを表示する
- リレーションの強調
- Oracleの
DBMS_METADATA.GET_DDL
で生成したDDLなど他DBMS対応 - (未実装) 力の強さを動的に変更できる
- (未実装) ファイル読み込み
- (未実装) リレーションを追加
- (未実装) カラムを変更してALTERクエリ生成
※他にもあればgithubまでissueをください。Pull requestならもっとうれしいです!
アーキテクチャ設計
- SPAのみで完結
- 試作なので面倒なことは考えたくない
- DDLパーサ
- jsでDDLをパースするニーズが無いのか見当たらず、諦めて雑なパーサを書きました
- d3.js
- 枯れてそう
- カスタマイズが考慮されていそう
シンプルな構成にしました。
- HTML textareaからDDLを取得
- DDPパーサでjsのobjectにコンバート
- d3.jsに食わせる
実装
実装は黙々とするだけですが、悩んだポイントです。
線と複数行テキスト
d3.jsはHTMLでもHTML canvasでもsvgでも好きな表現方法を選べますが、
- HTMLだと複数行テキストを書けるが、線を書くのが大変
- canvasだと複数行テキストを書くのが大変だが、線を書くのは簡単
- svgだと複数行テキストを書くのが大変だが、線を書くのは簡単
でした。
複数行テキストの描画を優先して、HTMLを選択しました。そのため線の描画を頑張って実装しています。
次のコードは、jsでHTML,CSSを使い、ピクセル単位で意図した線を描くコード例です。
// 1. 線の始点、長さ、角度をまとめておく
function calcLine(source, target) {
const dx = target.x - source.x;
const dy = target.y - source.y;
return {
x: source.x, // 始点はそのまま
y: source.y,
length: Math.sqrt(dx ** 2 + dy ** 2), // 長さは三平方の定理
rad: Math.atan2(dy, dx) * 180 / Math.PI, // CSS transformのrotateを使うのでラジアンにしておく
};
}
// 右上がりの線
const line = calcLine({x: 20, y: 100}, {x: 230, y: 10});
// 2. 線を書くためのblock要素を作る
const block = document.createElement('div');
block.style.backgroundColor = 'black'; // 背景色が線の色、点線にしたければ背景色を工夫する
block.style.height = '2px'; // 高さが線の幅
document.body.appendChild(block); // 試してる感を出すために、足しちゃう。本当は全部終わってからDOM描画したほうが良い
// 3. 長さをあわせる
block.style.width = `${line.length}px`;
// 4. 角度にあわせる
// rotateZは要素の中心から回転させてしまうので、中心が始点になるように移動させておく
block.style.transform = `rotateZ(${line.rad}deg) translate(${line.length / 2}px)`;
// 5. 始点に置く
// 4でずらした分も、ここで戻す
block.style.left = `${line.x - line.length / 2}px`;
block.style.top = `${line.y}px`;
まとめ
よいサービスを作るためには、よいドキュメントが必要不可欠です。一方、プロダクトが成長していくと、システムはどんどん複雑化していき、ドキュメント化が困難になりメンテナンスが追いつかなくなっていきます。
今回試作してみて、コードを静的解析した結果から、役に立つドキュメントの生成に可能性を感じました。
ラクーンでは一緒によいサービスを作る仲間を募集中です!
もしご興味を持っていただけましたら、こちらからエントリーお待ちしています!カジュアル面談もやってます。