q-BPM.orgにおけるBPMN記法に基づくプロセスダイアグラム画像の多くは、Graphvizエクステンションによって生成されている。ここではGraphvizを用いたBPMN図の作成方法について記述する。
目次 |
Graphvizの概要
| OSSライセンスの種類 |
|---|
|
Graphvizは「グラフ」を描画するためのオープンソースソフトウェア(OSS)で、AT&T Researchにより公開されている(CPL1.0ライセンス)。グラフ表記言語DOTによる記述を解読し、画像ファイルを生成する。DOTの[公式マニュアル]に記載されているサンプルは以下の通り。
Graphvizのツール群
Graphvizは、有向グラフ、無向グラフ、放射状グラフ、循環グラフなどを描画できる。(ここでは「BPMN記法を用いたBPDを定義する事」に主眼を置くため、有向グラフの作成を前提に記述する)
グラフとは
そもそも「グラフ」とは、「グラフ理論(Graph Theory)」分野における専門用語で「ノードとエッジによって表現できるもの」を指す。ビジネスプロセスダイアグラム(BPD)の様な流れ図に限らず、依存関係、データ構造、地図情報など様々な情報を模式化する為に広く利用される。
流れ図であるBPDを描くとき、そのエッジには向きが定義されるが、グラフ理論において、その様なグラフを特に「有向グラフ(Directed Graph)」と呼ぶ。その逆にエッジに向きが無いものを「無向グラフ(Undirected Graph)」、またエッジ交差がなく表記できるものを「平面グラフ(Plane Graph)」、すべてのエッジが水平垂直方向のみの直線で表現できるものを「直交グラフ(Orthogonal Graph)」と呼ぶ。
公式サンプルA
<DOT表記>
digraph test123 {
a -> b -> c;
a -> {x y};
b [shape=box];
c [label="hello\nworld",color=blue,fontsize=24,fontname="Palatino-Italic",fontcolor=red,style=filled];
a -> z [label="hi", weight=100];
x -> z [label="multi-line\nlabel"];
edge [style=dashed,color=red];
b -> x;
{rank=same; b x}
}
公式サンプルB
<DOT表記>
graph test123 {
a -- b -- c;
a -- {x y};
x -- c [w=10.0];
x -- y [w=5.0,len=3];
}
BPMN記法の例
Graphvizは、BPMN記法におけるオブジェクトやマーカの全てを表現できるものではなく、また厳密に表現できるものでもない。しかしながらテキスト情報を元に画像を自動的に生成するため、BPD画像作成やBPD画像翻訳の効率を高められる。 当サイトでは以下の書式を推奨する。
グラフの全体属性
有向グラフを描くために「digraph」、流れる方向を左から右へとする(デフォルトは上から下)ために「rankdir=LR;」を指定する必要がある。
シーケンスフロー
| 接続オブジェクト |
|---|
- 非制御フロー
- アクティビティ「A1」が完了すれば、後続の「A2」「A3」が共に同時に実行される(AND-Split)。特別なエッジ属性を指定する必要はない。
- 制御フロー
- 単一選択分岐(XOR-Split)等で利用される。「A2」完了時、条件「x>100」が真であれば「A4」が実行される。エッジ属性として「arrowtail=odiamond」を指定する。また条件文は「label」「headlabel」「taillabel」の値として指定する。
- デフォルトフロー
- 制御フローと組み合わせて使用され、全ての制御フローの条件が満たされない場合に選択される。エッジ属性として「arrowtail=rcrowlvee」を指定する。(バックスラッシュの向きを逆転させたい場合には「arrowtail=lcrowrvee」)
<DOT表記>
digraph G {
rankdir=LR;
A1 -> {A2 A3};
A2 -> A4 [arrowtail=odiamond, label="X>100"];
A2 -> A5 [arrowtail=rcrowlvee];
}
アクティビティ
| フローオブジェクト |
|---|
角が丸い長方形を表現するため、ノード属性「shape=box, style=rounded」を指定する。ラベルを変更する場合には「label」の値として指定する。
<DOT表記>
digraph G {
rankdir=LR;
A1 [shape=box, style=rounded];
A2 [shape=box, style=rounded, label=""];
A3 [shape=box, style=rounded, label="申請"];
A1 -> A2 -> A3 ;
}
全ノードのデフォルト値を変更する場合は、予約語「node」により設定する。
<DOT表記>
digraph G {
rankdir=LR;
node [shape=box, style=rounded];
A1 ;
A2 [label=""];
A3 [label="申請"];
A1 -> A2 -> A3 ;
}
イベント
「ラベルの無い小さめの円」を表現するため、ノード属性を「label="", shape=circle, width=0.3」と指定する(デフォルトのwidthは0.75)。終了イベントは太線円にするため、加えて「style=bold」を指定する。
<DOT表記>
digraph G {
rankdir=LR;
node [shape=box, style=rounded];
AS [label="", shape=circle, width="0.3"];
AE [label="", shape=circle, width="0.3", syle=bold];
A1 ;
A2 [label=""];
A3 [label="申請"];
AS -> A1 -> A2 -> A3 -> AE;
}
スイムレーン
グラフの中にスイムレーン(サブグラフ)を作るために「subgraph clusterXXX」を記述する。(subgraphの名前の最初にclusterが付く。サブグラフのクラスタ機能と言う)。またスイムレーンのパーティシパント(参加者/関与システム)を記述すべく「label="marketing@company.com";」、また左寄せにすべく「labeljust=l;」を指定する。
<DOT表記>
digraph G {
rankdir=LR;
node [shape=box, style=rounded];
subgraph clusterA {
labeljust=l;
label="marketing@company.com";
AS [label="", shape=circle, width="0.3"];
AE [label="", shape=circle, width="0.3", style=bold];
A1 [label="A1:文書作成"];
A2 [label="A2:修正確認"];
AS -> A1;
A1 -> A2 [style=invis]; // 不可視のエッジを定義し、AS、A1、A2、AEが水平に並ぶようにする
A2 -> AE;
}
subgraph clusterB {
labeljust=l;
label="marketing-leader@company.com";
B1 [label="B1:文書確認"];
B2 [label="B2:コメント"];
B1 -> B2;
}
A1 -> B1; // スイムレーンをまたぐシーケンスフローはクラスタ定義の外に書く
B2 -> A2;
}
なお、sytle、color、fillcolor等の値を設定する事で「クラスタの外見」を変更する事も出来る。更にトップレベルグラフでcompound属性をtrue に設定し、アクティビティ(ノード)とスイムレーン(クラスタ)を結ぶメッセージフロー(エッジ)を描くこともできる。(詳細は参考文献)
Tips
位置を揃える(invisエッジの追加)
原則としてアクティビティの配置は、左から右へ並べられる。しかし上記の例においては、A1とB1を同じX座標(同じランク)にしたいと思う。A1とB2の間に「張力を与える」事で、A1とB1を同じランクにする事ができる。 なお、同じグラフ内(クラスタ内)のアクティビティであれば、「{rank=same; A1 A2}」と言う様な指定が出来る(「ランクの指定」参照)
<DOT表記>
digraph G01 {
rankdir=LR; node [shape=box, style=rounded];
subgraph clusterA {
labeljust=l; label="marketing@company.com";
AS [label="", shape=circle, width="0.3"];
AE [label="", shape=circle, width="0.3", style=bold];
A1 [label="A1:文書作成"];
A2 [label="A2:修正確認"];
AS -> A1;
A1 -> A2 [style=invis];
A2 -> AE;
}
subgraph clusterB {
labeljust=l; label="marketing-leader@company.com";
B1 [label="B1:文書確認"];
B2 [label="B2:コメント"];
B1 -> B2;
}
A1 -> B1;
B2 -> A2;
A1 -> B2 [style=invis]; // ★不可視のエッジにより、A1とB2を近づける★
}
位置を揃える(張力の指定)
特定のアクティビティ「A1」「A2」「A3」を直線的に並べたい場合、分岐部分で“本線”たる「A1->A2」に「weight=10」の張力(重み)を指定する。
<DOT表記>
digraph G02 {
rankdir=LR; node [shape=box, style=rounded];
subgraph clusterA {
labeljust=l; label="person@company.com";
AS [label="", shape=circle, width="0.3"];
AE1 [label="", shape=circle, width="0.3", style=bold];
AE2 [label="", shape=circle, width="0.3", style=bold];
A1 [label="A1:activity"];
A2 [label="A2:activity"];
A3 [label="A3:activity"];
Ae [label="Ae:activity"];
AS -> A1;
A1 -> Ae [arrowtail=odiamond, label="NG"];
Ae -> AE1;
A1 -> A2 [arrowtail=rcrowlvee, weight=10]; //★張力「weight=10」により「A1->Ae」より「A1->A2」が直線的になる★
A2 -> A3 [style=invis];
A3 -> AE2 ;
}
subgraph clusterB {
labeljust=l; label="section-leader@company.com";
B1 [label="B1:activity"];
B2 [label="B2:activity"];
B1 -> B2;
}
A2 -> B1;
B2 -> A3;
A2->B2 [style=invis];
}
位置を揃える(ランクの指定)
サブグラフ「clusterA」内で、「Ae」と「A3」のX座標(ランク)を合わせるために「{rank=same Ae A3};」を指定する。
<DOT表記>
digraph G03 {
rankdir=LR; node [shape=box, style=rounded];
subgraph clusterA {
labeljust=l; label="person@company.com";
AS [label="", shape=circle, width="0.3"];
AE1 [label="", shape=circle, width="0.3", style=bold];
AE2 [label="", shape=circle, width="0.3", style=bold];
A1 [label="A1:activity"];
A2 [label="A2:activity"];
A3 [label="A3:activity"];
Ae [label="Ae:activity"];
AS -> A1;
A1 -> Ae [arrowtail=odiamond, label="NG"];
Ae -> AE1;
A1 -> A2 [arrowtail=rcrowlvee, weight=10];
A2 -> A3 [style=invis];
A3 -> AE2 ;
{rank=same Ae A3}; // ★「Ae」と「A3」のランクを合わせる★
}
subgraph clusterB {
labeljust=l; label="section-leader@company.com";
B1 [label="B1:activity"];
B2 [label="B2:activity"];
B1 -> B2;
}
A2 -> B1;
B2 -> A3;
A2->B2 [style=invis];
}
位置を揃える(最低距離の指定)
「Ae」から「終了イベント」に伸びるシーケンスフローに「minlen=2」を指定する事で、“ランク”を挿入する事ができる。二つある終了イベントを並べる事ができる。(※二つの終了イベントに対して「ランクを揃える」を実行しても結果は同様)
<DOT表記>
digraph G04 {
rankdir=LR; node [shape=box, style=rounded];
subgraph clusterA {
labeljust=l; label="person@company.com";
AS [label="", shape=circle, width="0.3"];
AE1 [label="", shape=circle, width="0.3", style=bold];
AE2 [label="", shape=circle, width="0.3", style=bold];
A1 [label="A1:activity"];
A2 [label="A2:activity"];
A3 [label="A3:activity"];
Ae [label="Ae:activity"];
AS -> A1;
A1 -> Ae [arrowtail=odiamond, label="NG"];
Ae -> AE1 [minlen=2]; //★「minlen=2」(inch)により「AE1」と「Ae」の間に一定の距離が置かれる★
A1 -> A2 [arrowtail=rcrowlvee, weight=10];
A2 -> A3 [style=invis];
A3 -> AE2 ;
}
subgraph clusterB {
labeljust=l; label="section-leader@company.com";
B1 [label="B1:activity"];
B2 [label="B2:activity"];
B1 -> B2;
}
A2 -> B1;
B2 -> A3;
A2->B2 [style=invis];
}
フローの重なりを回避する(ポートの指定)
「A1」と「B1」が同じX座標にあって、かつ「A1→B1」と「B1→A1」の様な往復するシーケンスフローを書きたい場合、重なりを排除するために、どちらかのシーケンスフローのポート位置を「[tailport=sw,headport=nw];」(矢先が南西、矢尻が北西)の様に指定する必要がある(「n、nw、w、sw、s、se、e、ne」デフォルトは「center」)。(ただしランクをずらす方が綺麗に見える)
<DOT表記>
digraph G05 { rankdir=LR; node [shape=box, style=rounded];
subgraph clusterA { labeljust=l; label="any-section@company.com";
AS [label="", shape=circle, width="0.3"];
AE [label="", shape=circle, width="0.3", style=bold];
A1 [label="A1: Daily\nReport"];
A2 [label="A2: Memo"];
AS -> A1;
A2 -> AE;
A1 -> A2 [style=invis];
}
subgraph clusterB { labeljust=l; label="section-leader@company.com";
B1 [label="B1: Review"];
}
A1 -> B1 [tailport=sw,headport=nw]; // ★テールとヘッドについてポート位置を指定する★
B1 -> A1 [arrowtail=odiamond, label="NG"];
B1 -> A2 [arrowtail=rcrowlvee];
}
ランクをずらした例 (「不可視のエッジ(B1 -> AE [style=invis, weight=10];)」を追加)
フローを逆行させる(dir属性の変更)
原則として、アクティビティやイベント(ノード)は、シーケンスフローが全て右向きになる様に配置される。あえて左向きのシーケンスフローを描きたい場合には「[dir=back]」を指定する。(以下の例では、[labelfloat=true]を設定し、シーケンスフローとラベルの重なりを許可している)
<DOT表記>
digraph G06 { rankdir=LR; node [shape=box, style=rounded]; edge [labelfloat=true];
subgraph clusterA { labeljust=l; label="any-section@company.com";
AS [label="", shape=circle, width="0.3"];
AE [label="", shape=circle, width="0.3", style=bold];
A1 [label="A1: Daily\nReport"];
A2 [label="A2: Confirm\n& Memo"];
AS -> A1;
A1 -> AE [arrowtail=rcrowlvee];
AE -> A2 [dir=back]; //★フローの逆行指定★
}
subgraph clusterB { labeljust=l; label="section-leader@company.com";
B1 [label="B1: Review"];
}
A1 -> B1 [arrowtail=odiamond, label="Review Flag"];
B1 -> A1 [arrowtail=odiamond, taillabel="NG", tailport=w, headport=s];
B1 -> A2 [arrowtail=rcrowlvee];
}
グラフ全体のサイズを指定する
プロセスダイアグラムは横方向に長くなりがちであるが、画面の都合上、5~7ランク程度に収めたい。(文字列も改行「\n」を多用し横方向に長くならない様に努めたい)。やむを得ずダイアグラムが大きくなる場合には「[size="10,5"]」の様にグラフサイズの最大値を指定し、全体を縮尺する事が出来る。(横方向の最大値は10インチを推奨する)。以下、サイズ制限をしたケースと、していないケースを例示する。
<DOT表記>
digraph G07 { graph [size="10,5", rankdir=LR]; node [shape=box, style=rounded]; edge [labelfloat=true];
subgraph clusterA { labeljust=l; label="marketing@company.com";
MS [label="", shape=circle, width="0.3"];
ME [label="", shape=circle, width="0.3", style=bold];
M1 [label="M1:step1"];
M2 [label="M2:step2"];
M3 [label="M3:step3"];
M4 [label="M4:step4"];
M5 [label="M5:step5"];
M6 [label="M6:step6"];
M7 [label="M7:step7"];
MS -> M1;
M1 -> M2 -> M3 -> M4 -> M5 -> M6 -> M7 [style=invis];
M7 -> ME;
}
subgraph clusterB { labeljust=l; label="development@company.com";
D1 [label="D1:step1"];
D2 [label="D2:step2"];
D3 [label="D3:step3"];
D4 [label="D4:step4"];
D5 [label="D5:step5"];
D6 [label="D6:step6"];
D1 -> D2 -> D3 -> D4 -> D5 -> D6 [style=invis];
}
M1 -> D1 -> M2 -> D2 -> M3 -> D3 -> M4 -> D4 -> M5 -> D5 -> M6 -> D6 -> M7;
}
作図雛型
描画方法の例が多用されている雛型。(BPMN図としては成立していない)
<graphviz>
digraph TMPG { graph [size="10,5", rankdir=LR]; node [shape=box, style=rounded]; edge [labelfloat=true];
subgraph clusterA { labeljust=l; label="sectionA@company.com";
AS [label="", shape=circle, width="0.3"];
AE [label="", shape=circle, width="0.3", style=bold];
A1 [label="A1:step1"];
A2 [label="A2:step2"];
A3 [label="A3:step3"];
A4 [label="A4:step4"];
Ae [label="Ae:error\ncase"];
A5 [label="A5:step5\nLong Name"];
AS -> A1;
A5 -> AE [weight=10];
A1 -> A2 -> A3 -> A4 -> A5 [style=invis,weight=10];
A4 -> Ae;
Ae -> AE;
{rank=same Ae A5};
}
subgraph clusterB { labeljust=l; label="sectionB@company.com";
B1 [label="B1:step1"];
B2 [label="B2:step2"];
B3 [label="B3:step3"];
B4 [label="B4:step4", style="rounded,filled" fillcolor=red];
B5 [label="B5:step5", style="rounded,filled" fillcolor=green];
B1 -> B2 -> B3 -> B4 -> B5 [style=invis];
}
subgraph clusterBL { labeljust=l; label="sectionB-leader@company.com";
BL1 [label="BL1:step1"];
BL2 [label="BL2:Message", shape=ellipse, style=dotted];
BL3 [label="BL3:TEXT", shape=none];
BL1 -> BL2 [style=invis];
BL2 -> BL3 [minlen=2];
}
A1 -> B1 [arrowtail=odiamond, label="X>100"];
B1 -> BL1 [arrowtail=rcrowlvee];
B2 -> A4 [tailport=ne,headport=w];
B2 -> BL2 [arrowhead=onormal, style=dotted]
}
</graphviz>
参考文献
- Graphviz - Graph Visualization Software 本家サイト(英文)
- dot User's Manual マニュアル(January 26, 2006)(PDF;40p)(英文)






