Skip to content

Instantly share code, notes, and snippets.

@nota-ja
Last active December 4, 2022 23:00
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nota-ja/7f54b5489b87ac790f484ce45cd4c662 to your computer and use it in GitHub Desktop.
Save nota-ja/7f54b5489b87ac790f484ce45cd4c662 to your computer and use it in GitHub Desktop.
Mermaid (+ Git) のススメ

Mermaid (+ Git) のススメ

この記事は, NTT Communications Advent Calendar 2022 5日目の記事です。

Mermaid の紹介記事は巷に溢れているので今更感はありますが,自分なりの感想を交えつつオススメのポイントなどを書いてみたいと思います。

Mermaidとは

公式サイト https://mermaid-js.github.io/mermaid/ より:

Mermaid lets you create diagrams and visualizations using text and code.

ということで,「テキストとコードで作図できるツール」です。

上の公式サイトを見てもらえればわかる通り,

  • フロー図
  • シーケンス図
  • クラス図
  • 状態遷移図
  • ER図

などのソフトウェアの設計に必要な種々の図のほか,

  • ユーザー・ジャーニー
  • ガント・チャート
  • 円グラフ
  • 要求図
  • Gitブランチのグラフ
  • マインドマップ

まで,本当にさまざまな図が書けます (私自身で書いたことがあるのはフロー図, シーケンス図, 状態遷移図, ガントチャートの4種類)。

今はMarkdownに埋め込んで使われることが多いのですが (実際自分もそうしている),本体はJavaScriptで書かれた作図ツールなので,本来的にはMarkdownに限定されないはずです。

なぜMermaidで書くのか

版管理/差分管理ができるから。これに尽きます。

例えば,仕様定義の際,こんなシーケンス図 (V0) を書いたとします (以下のシーケンス図のソースは, ここ にも置いています)。

sequenceDiagram
    actor Alice
    participant Source 1
    participant Source 2
    participant Service A
    participant Database A

    Alice->>Source 1: 情報1取得
    Source 1->>Alice: 
    Alice->>Source 2: 情報2取得
    Source 2->>Alice: 
    Alice->>Alice: 結果=情報1+情報2
    Alice->>Service A: 結果格納
    Service A->>Database A: 結果格納

ソース:

sequenceDiagram
    actor Alice
    participant Source 1
    participant Source 2
    participant Service A
    participant Database A

    Alice->>Source 1: 情報1取得
    Source 1->>Alice: 
    Alice->>Source 2: 情報2取得
    Source 2->>Alice: 
    Alice->>Alice: 結果=情報1+情報2
    Alice->>Service A: 結果格納
    Service A->>Database A: 結果格納

その後の検討の結果「クライアント側で加算を行う必要はない。計算機にやらせるべき」ということで,以下のように変更 (V1) され,

sequenceDiagram
    actor Alice
    participant Source 1
    participant Source 2
    participant Service A
    participant Database A

    Alice->>Source 1: 情報1取得
    Source 1->>Alice: 
    Alice->>Source 2: 情報2取得
    Source 2->>Alice: 
    Alice->>Service A: 情報1, 情報2
    Service A->>Service A: 結果=情報1+情報2
    Service A->>Database A: 結果格納

ソース:

sequenceDiagram
    actor Alice
    participant Source 1
    participant Source 2
    participant Service A
    participant Database A

    Alice->>Source 1: 情報1取得
    Source 1->>Alice: 
    Alice->>Source 2: 情報2取得
    Source 2->>Alice: 
    Alice->>Service A: 情報1, 情報2
    Service A->>Service A: 結果=情報1+情報2
    Service A->>Database A: 結果格納

V0 -> V1 の差分:

diff --git sequence-example.md sequence-example.md
index b3ea99a..1162095 100644
--- sequence-example.md
+++ sequence-example.md
@@ -10,7 +10,7 @@ sequenceDiagram
     Source 1->>Alice:
     Alice->>Source 2: 情報2取得
     Source 2->>Alice:
-    Alice->>Alice: 結果=情報1+情報2
-    Alice->>Service A: 結果格納
+    Alice->>Service A: 情報1, 情報2
+    Service A->>Service A: 結果=情報1+情報2
     Service A->>Database A: 結果格納

さらに,もっと汎用性を上げる (複数回の情報の加算にも対応) ということで,以下のように変更 (V2) されたとします。

sequenceDiagram
    actor Alice
    participant Source 1
    participant Source 2
    participant Service A
    participant Database A

    Alice->>Source 1: 情報1取得
    Source 1->>Alice: 
    Alice->>Service A: 情報1格納
    Service A->>Database A: 情報1格納
    Alice->>Source 2: 情報2取得
    Source 2->>Alice: 
    Alice->>Service A: (情報1に) 情報2を加算更新
    Service A->>Database A: 情報1取得
    Database A->>Service A: 
    Service A->>Service A: 結果=情報1+情報2
    Service A->>Database A: 結果を情報1として更新
    Alice->>Source 1: 情報3取得
    Source 1->>Alice: 
    Alice->>Service A: (情報1に) 情報3を加算更新
    Service A->>Database A: 情報1取得
    Database A->>Service A: 
    Service A->>Service A: 結果=情報1+情報3
    Service A->>Database A: 結果を情報1として更新

ソース:

sequenceDiagram
    actor Alice
    participant Source 1
    participant Source 2
    participant Service A
    participant Database A

    Alice->>Source 1: 情報1取得
    Source 1->>Alice: 
    Alice->>Service A: 情報1格納
    Service A->>Database A: 情報1格納
    Alice->>Source 2: 情報2取得
    Source 2->>Alice: 
    Alice->>Service A: (情報1に) 情報2を加算更新
    Service A->>Database A: 情報1取得
    Database A->>Service A: 
    Service A->>Service A: 結果=情報1+情報2
    Service A->>Database A: 結果を情報1として更新
    Alice->>Source 1: 情報3取得
    Source 1->>Alice: 
    Alice->>Service A: (情報1に) 情報3を加算更新
    Service A->>Database A: 情報1取得
    Database A->>Service A: 
    Service A->>Service A: 結果=情報1+情報3
    Service A->>Database A: 結果を情報1として更新

V1 -> V2 の差分:

diff --git sequence-example.md sequence-example.md
index 1162095..6a14512 100644
--- sequence-example.md
+++ sequence-example.md
@@ -8,9 +8,20 @@ sequenceDiagram

    Alice->>Source 1: 情報1取得
    Source 1->>Alice:
+    Alice->>Service A: 情報1格納
+    Service A->>Database A: 情報1格納
    Alice->>Source 2: 情報2取得
    Source 2->>Alice:
-    Alice->>Service A: 情報1, 情報2
+    Alice->>Service A: (情報1に) 情報2を加算更新
+    Service A->>Database A: 情報1取得
+    Database A->>Service A:
    Service A->>Service A: 結果=情報1+情報2
-    Service A->>Database A: 結果格納
+    Service A->>Database A: 結果を情報1として更新
+    Alice->>Source 1: 情報3取得
+    Source 1->>Alice:
+    Alice->>Service A: (情報1に) 情報3を加算更新
+    Service A->>Database A: 情報1取得
+    Database A->>Service A:
+    Service A->>Service A: 結果=情報1+情報3
+    Service A->>Database A: 結果を情報1として更新

これらのソースをGit等の版管理ツールで管理すれば,図の

  • どの部分を
  • いつ
  • どういう理由で (commit message に書く)

変更したかの管理が極めて容易に行えます。

"テキストとコード" で図を記述することの最大のメリットは,1番目の「どの部分を変更したか」が明確にわかる点です。

もちろんPNG等の図としてGit管理することも可能ですが,その場合Gitは図の差分を取ってくれないので,「どこを変更したか」は人間の目で差分を取るしかありません。

これが "テキストとコード" であれば,差分もGitが管理してくれるので,「どこを変更したか」が明確に認識できます。

また,版管理ツールで管理するメリットとしては3番目の「どういう理由で変更したか」が残る点が大きいと考えられます。後述するMiroでも履歴という形で過去の版に遡ることはできますが,「なぜそのような変更を行ったのか」を変更自体と紐付けて管理することはなかなか難しいものがあるように思います。

Mermaidの使い方(あるいは使わない方が良いケース)

ここまでは「Mermaid (+ Git) 万歳!」という話をしてきましたが,実は私は仕事で Miro も使っています。

両方使ってみた結果,両者は使い分けるのいいのではないかと感じました。

まだイメージが固まっていない,仕様/設計検討の初期段階では,可能ならリアルに集まって模造紙やホワイトボードを使い,オンラインであればMiro等のツールを使って共同編集しながら議論を進める方がやりやすいのではないかと思います。

そして,一旦イメージが固まり,何らかの理由でチーム外に図を共有するタイミングでMermaid化し,以後それをベースに版管理を行う,というのが良さそうです。

なお,図の見映えにこだわる場合,Mermaidを使わない方が良さそうに思います。

私個人は図が意味的に正しければあまり気にしないのですが,特に細部の見映えにこだわる人には,Mermaidは向いていません。

シーケンス図だとあまりこだわりポイントはなさそうで,あえていうと参加者の順番くらいかと思いますが,これは冒頭で宣言することで宣言順に並べることができます(宣言しないと出現順)。

例えば以下の図は前節のV0の例の参加者順を変更したもの (V0.1) です。

sequenceDiagram
    participant Source 1
    participant Source 2
    actor Alice
    participant Service A
    participant Database A

    Alice->>Source 1: 情報1取得
    Source 1->>Alice: 
    Alice->>Source 2: 情報2取得
    Source 2->>Alice: 
    Alice->>Alice: 結果=情報1+情報2
    Alice->>Service A: 結果格納
    Service A->>Database A: 結果格納

ソース:

sequenceDiagram
    participant Source 1
    participant Source 2
    actor Alice
    participant Service A
    participant Database A

    Alice->>Source 1: 情報1取得
    Source 1->>Alice: 
    Alice->>Source 2: 情報2取得
    Source 2->>Alice: 
    Alice->>Alice: 結果=情報1+情報2
    Alice->>Service A: 結果格納
    Service A->>Database A: 結果格納

これと V0 との差分は以下です。

diff --git sequence-example.md sequence-example.md
index b3ea99a..20b4d4f 100644
--- sequence-example.md
+++ sequence-example.md
@@ -1,8 +1,8 @@
 ```mermaid
 sequenceDiagram
-    actor Alice
     participant Source 1
     participant Source 2
+    actor Alice
     participant Service A
     participant Database A

宣言部分の順番を変えただけであることがおわかりいただけるかとおもいます。

もちろんシーケンス図でも,データベースのアイコンをシリンダーにしたいとか, actor のアイコンにPowerPointの人形アイコンを使いたいとか,その種の細かい点にこだわりだすとキリがないとは思います(すごく頑張れば=CSSを書き換えれば可能かもしれませんが……)。

若干脱線しましたが,話を元に戻すと,シーケンス図よりもこだわりが表れやすそうなものとしては,例えば状態遷移図があります。

stateDiagram-v2
	Applied: チケット確認中
	Cancelled: キャンセル
	Accepted: チケット発行
	Attending: チケット有効
	Finished: チケット無効

    [*] --> Applied: イベント参加申込
	Applied --> Accepted: 申込受付完了
	Accepted --> Attending: イベント参加
	Attending --> Finished: イベント終了
	Accepted --> Finished: イベント不参加
    Finished --> [*]
    Applied --> Cancelled: 申込キャンセル
    Cancelled --> [*]

ソース:

stateDiagram-v2
	Applied: チケット確認中
	Cancelled: キャンセル
	Accepted: チケット発行済み
	Attending: チケット有効
	Finished: チケット無効

    [*] --> Applied: イベント参加申込
	Applied --> Accepted: 申込受付完了
	Accepted --> Attending: イベント参加
	Attending --> Finished: イベント終了
	Accepted --> Finished: イベント不参加
    Finished --> [*]
    Applied --> Cancelled: 申込キャンセル
    Cancelled --> [*]

上のような状態遷移図で,例えば 「【キャンセル】の位置をもっと上にしたい」と思ったとしても,配置はMermaid側で行われるので修正はまず無理です。

この種の「位置を変えたい」や「色を変えたい」「フォントを変えたい」あるいは「長い行を途中で改行したい」というようなこだわりは図を描く場合ありがちですが,そこが割り切れない人はMermaidには向いてないと思われます。

(おまけ) Mermaidで描かれた図形のファイル出力

現在,私はMermaidを使う場合, Visual Studio Code を使い,拡張機能として

(以前 Mermaid Export も入れていたが,機能しなかったので外した)

を入れているのですが,図の出力がうまくいきません (【Open Preview Enhanced】が機能しない。おそらく M1 chip 絡みの問題ではないかと憶測している)。

そういう状況で, Mermaidで描いた図形を画像ファイルに出力し、キレイに拡大表示してみた という記事を見つけ,ちょうど Finchも発表された ので,これと組み合わせて試してみようと思ったのですが,この Advent Calendar には間に合いませんでした。

後日試せたら,結果を改めて追記したいと思います(といいつつ書かないかもしれないけど,実際困っているので少なくとも試すだけはやるはず)。

それでは,明日の NTT Communications Advent Calendar 2022 もお楽しみに。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment