- 設計
DFD(データフロー図)の書き方と記号|コンテキスト図・レベル0の例
DFD(データフロー図)とは、システムの中を「どんなデータが、どこから来て、どこで加工され、どこへ行くか」を4種類の記号だけで表す図です。処理の順番や条件分岐は書かず、データの流れだけを書きます。 記号が少ないので、開発者でない人とも同じ図で話ができます。
この記事では、記号の対応表と接続ルールの早見表を示したあと、架空の備品貸出システムを題材に、コンテキスト図・レベル0・レベル1を Mermaid で順に書きます。上下の階層で入出力がそろっているかを調べるチェック表と Python コード、同じ題材のフローチャートとの比較も載せています。
確認環境:Ubuntu 24.04、Mermaid 11.14.0(@mermaid-js/mermaid-cli 11.14.0 と Chromium 141 で SVG に描画)、Python 3.11。
DFD の記号と接続ルールの早見表
4つの記号(デマルコ式とゲイン・サーソン式の対応)
| 要素 | 役割 | デマルコ式(ヨードン・デマルコ式) | ゲイン・サーソン式 | この記事の Mermaid での書き方 |
|---|---|---|---|---|
| 外部実体(源泉・吸収先) | システムの外にいて、データを出す人・受け取る人・他システム | 長方形 | 正方形(影つきで描くことが多い) | U[利用者] 長方形 |
| プロセス(処理) | 入力データを加工して出力データにする | 円(バブル) | 角の丸い長方形(上段に番号) | P1((1 貸出を受け付ける)) 円 |
| データストア | データをためておく場所(台帳、ファイル、テーブル) | 2本の平行線 | 右側が開いた長方形(左端に番号) | D1[(D1 備品台帳)] 円柱 |
| データフロー | データの流れ。名前は名詞で書く | 名前つきの矢印 | 名前つきの矢印 | A -->|名前| B |
どちらの方式でも要素は同じ4つで、形が違うだけです。1つの文書の中では方式をそろえます。Mermaid には2本の平行線の記号が無いので、この記事ではデータストアを円柱で表しています。
接続の可否(行が出発点、列が到着点)
| 出発 \ 到着 | 外部実体 | プロセス | データストア |
|---|---|---|---|
| 外部実体 | 書かない(システムの外の話) | できる | できない(プロセスを通す) |
| プロセス | できる | できる | できる |
| データストア | できない(プロセスを通す) | できる | できない(プロセスを通す) |
覚え方は「矢印の両端のどちらかは必ずプロセス」です。 データは誰かが加工したり運んだりしないと動かないので、外部実体やデータストアどうしを直接つなぐ矢印は書きません。
階層(レベル)の早見表
| 図 | 書く内容 | プロセスの数 | データストア |
|---|---|---|---|
| コンテキスト図 | システム全体を1つのプロセスにし、外部実体とのやり取りだけを書く | 1個(番号 0) | 書かない |
| レベル0 | システムを主要な機能に分ける | 3〜7個程度(番号 1、2、3…) | 書く |
| レベル1 | レベル0のプロセス1つをさらに分ける | 3〜7個程度(番号 1.1、1.2…) | 書く |
コンテキスト図を「レベル0」と呼び、その次の図を「レベル1」と呼ぶ流儀もあります。文書の最初に、どちらの呼び方を使うかを書いておくと読み手が迷いません。この記事では上の表の呼び方を使います。
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「DFD(データフロー図)の書き方と記号|コンテキスト図・レベル0の例」
DFD の書き方:5つの手順
| 手順 | やること | 成果物 |
|---|---|---|
| 1. 外部実体を決める | システムにデータを渡す人・受け取る人・他システムを並べる | 外部実体の一覧 |
| 2. 入出力のデータを決める | 外部実体ごとに「渡すデータ」「受け取るデータ」を名詞で書く | コンテキスト図 |
| 3. 主要なプロセスに分ける | 入力データごとに、それを受け取る処理を「〜を〜する」で書く | レベル0のプロセス |
| 4. データストアを足す | 処理と処理の間でためておくデータを置く | レベル0 |
| 5. 細かくして、上下をそろえる | 中身が大きいプロセスを分解し、親の図と入出力を照らし合わせる | レベル1 |
題材は次の仕様です(架空の備品貸出システム)。
- 利用者は、ノートPCやプロジェクターなどの備品の貸出を申請し、使い終わったら返却を連絡する
- システムは、貸出の可否と返却期限を利用者に知らせる
- 備品管理者は、備品の情報を登録し、貸出状況のレポートを受け取る
コンテキスト図の例(Mermaid)
システム全体を1つの円にし、外部実体との間を流れるデータだけを書きます。
flowchart LR
U[利用者]
A[備品管理者]
S((0<br>備品貸出システム))
U -->|貸出申請| S
U -->|返却連絡| S
S -->|貸出可否の通知| U
S -->|返却期限のお知らせ| U
A -->|備品情報| S
S -->|貸出状況レポート| A
| 外部実体 | システムへ渡すデータ(入力) | システムから受け取るデータ(出力) |
|---|---|---|
| 利用者 | 貸出申請、返却連絡 | 貸出可否の通知、返却期限のお知らせ |
| 備品管理者 | 備品情報 | 貸出状況レポート |
コンテキスト図の役割は、システムの範囲を決めることです。 ここに出てこないデータは扱わない、と読み手に伝わります。データフローの名前は「貸出申請」のような名詞にし、「申請する」のような動作は書きません。
レベル0 の例(Mermaid)
コンテキスト図の円を、4つのプロセスと2つのデータストアに分けます。
flowchart LR
U[利用者]
A[備品管理者]
P1((1<br>貸出を受け付ける))
P2((2<br>返却を処理する))
P3((3<br>備品を登録する))
P4((4<br>貸出状況を集計する))
D1[(D1 備品台帳)]
D2[(D2 貸出記録)]
U -->|貸出申請| P1
P1 -->|貸出可否の通知| U
P1 -->|返却期限のお知らせ| U
D1 -->|在庫数| P1
P1 -->|在庫の引当| D1
P1 -->|貸出内容| D2
U -->|返却連絡| P2
D2 -->|貸出中の記録| P2
P2 -->|返却日| D2
P2 -->|在庫の戻し| D1
A -->|備品情報| P3
P3 -->|備品データ| D1
D1 -->|備品一覧| P4
D2 -->|貸出履歴| P4
P4 -->|貸出状況レポート| A
| 番号 | プロセス | 入力 | 出力 |
|---|---|---|---|
| 1 | 貸出を受け付ける | 貸出申請、在庫数 | 貸出可否の通知、返却期限のお知らせ、在庫の引当、貸出内容 |
| 2 | 返却を処理する | 返却連絡、貸出中の記録 | 返却日、在庫の戻し |
| 3 | 備品を登録する | 備品情報 | 備品データ |
| 4 | 貸出状況を集計する | 備品一覧、貸出履歴 | 貸出状況レポート |
プロセスどうしを直接つなぐ矢印が1本もない点に注目してください。「貸出を受け付ける」と「返却を処理する」は、貸出記録(D2)を通じてつながっています。プロセスの間にデータストアを置くと、処理のタイミングが別々でもよいことが図から読み取れます。
レベル1 の例(Mermaid)
レベル0の「1 貸出を受け付ける」を3つのプロセスに分けます。番号は親の番号を引き継いで 1.1、1.2、1.3 とします。
flowchart LR
U[利用者]
D1[(D1 備品台帳)]
D2[(D2 貸出記録)]
subgraph P1[1 貸出を受け付ける]
direction LR
P11((1.1<br>申請内容を確かめる))
P12((1.2<br>在庫を引き当てる))
P13((1.3<br>貸出を記録する))
end
U -->|貸出申請| P11
P11 -->|貸出可否の通知| U
P11 -->|確認済みの申請| P12
D1 -->|在庫数| P12
P12 -->|在庫の引当| D1
P12 -->|貸出可否の通知| U
P12 -->|引当済みの申請| P13
P13 -->|貸出内容| D2
P13 -->|返却期限のお知らせ| U
subgraph で囲んだ範囲が、親のプロセス1の中身です。枠の外にある利用者・備品台帳・貸出記録は、親の図でプロセス1とつながっていた相手だけを書きます。「確認済みの申請」「引当済みの申請」は枠の中だけを流れるデータなので、親の図には現れません。
上下の階層で入出力が一致するかのチェック表
親のプロセスに出入りするデータフローと、子の図で枠の外とやり取りするデータフローは、同じでなければなりません(バランシング、平衡と呼びます)。
コンテキスト図(プロセス0)とレベル0
| 向き | 相手 | データフロー | コンテキスト図 | レベル0 | 判定 |
|---|---|---|---|---|---|
| 入力 | 利用者 | 貸出申請 | あり | あり(プロセス1) | 一致 |
| 入力 | 利用者 | 返却連絡 | あり | あり(プロセス2) | 一致 |
| 入力 | 備品管理者 | 備品情報 | あり | あり(プロセス3) | 一致 |
| 出力 | 利用者 | 貸出可否の通知 | あり | あり(プロセス1) | 一致 |
| 出力 | 利用者 | 返却期限のお知らせ | あり | あり(プロセス1) | 一致 |
| 出力 | 備品管理者 | 貸出状況レポート | あり | あり(プロセス4) | 一致 |
レベル0のプロセス1とレベル1
| 向き | 相手 | データフロー | レベル0 | レベル1 | 判定 |
|---|---|---|---|---|---|
| 入力 | 利用者 | 貸出申請 | あり | あり(1.1) | 一致 |
| 入力 | D1 備品台帳 | 在庫数 | あり | あり(1.2) | 一致 |
| 出力 | 利用者 | 貸出可否の通知 | あり | あり(1.1、1.2) | 一致 |
| 出力 | 利用者 | 返却期限のお知らせ | あり | あり(1.3) | 一致 |
| 出力 | D1 備品台帳 | 在庫の引当 | あり | あり(1.2) | 一致 |
| 出力 | D2 貸出記録 | 貸出内容 | あり | あり(1.3) | 一致 |
Python で接続ルールと階層の一致を調べる
図が増えると目で照らし合わせるのに時間がかかります。上の Mermaid のコードを context.mmd、level0.mmd、level1.mmd として保存しておけば、次のコードで接続ルールと階層の一致をまとめて調べられます。ノードの形(長方形・円・円柱)から要素の種類を読み取っています。
import re
import sys
NODE = re.compile(r"(\w+)(\(\(|\[\(|\[)") # ID と開きかっこ
FLOW = re.compile(r"(\w+)[^-]*-->|([^|]+)|\s*(\w+)") # A -->|名前| B
KIND = {"((": "プロセス", "[(": "データストア", "[": "外部実体"}
def load(path):
"""Mermaid の flowchart から、ノードの種類とデータフローを取り出す"""
kinds, flows = {}, []
for line in open(path, encoding="utf-8"):
line = line.strip()
if line.startswith(("flowchart", "subgraph", "direction", "end")):
continue
for node_id, bracket in NODE.findall(line):
kinds.setdefault(node_id, KIND[bracket])
m = FLOW.search(line)
if m:
flows.append((m.group(1), m.group(2), m.group(3)))
return kinds, flows
def check_rules(path):
"""プロセスを通らない接続と、入力か出力が欠けたプロセスを探す"""
kinds, flows = load(path)
print(f"[接続ルール] {path}")
found = 0
for src, name, dst in flows:
if "プロセス" not in (kinds[src], kinds[dst]):
print(f" 要修正: {src} -> {dst}({name})は {kinds[src]} と {kinds[dst]} の直結")
found += 1
for node_id, kind in kinds.items():
if kind == "プロセス":
n_in = sum(1 for f in flows if f[2] == node_id)
n_out = sum(1 for f in flows if f[0] == node_id)
if n_in == 0 or n_out == 0:
print(f" 要修正: プロセス {node_id} の入力 {n_in} 本、出力 {n_out} 本")
found += 1
print(f" フロー {len(flows)} 本、指摘 {found} 件")
def boundary(path, outside, target=None):
"""図の外側(outside)とやり取りするフローを (向き, 相手, 名前) の集合で返す"""
kinds, flows = load(path)
result = set()
for src, name, dst in flows:
if target and target not in (src, dst):
continue
if src in outside and kinds[dst] == "プロセス":
result.add(("入力", src, name))
if dst in outside and kinds[src] == "プロセス":
result.add(("出力", dst, name))
return result
def check_balance(parent, child, target):
"""親の図のプロセス target と、それを分解した子の図で、入出力が一致するか調べる"""
parent_kinds, _ = load(parent)
child_kinds, _ = load(child)
outside = {n for n, k in child_kinds.items() if k != "プロセス" and n in parent_kinds}
p = boundary(parent, outside, target)
c = boundary(child, outside)
print(f"[階層の一致] {parent} の {target} と {child}")
for item in sorted(p | c):
mark = "一致" if item in p and item in c else ("親だけ" if item in p else "子だけ")
print(f" {mark}: {item[0]} {item[1]} {item[2]}")
print(f" 親 {len(p)} 本、子 {len(c)} 本 -> {'一致' if p == c else '不一致'}")
if __name__ == "__main__":
for path in sys.argv[1:]:
check_rules(path)
if len(sys.argv) == 1:
for path in ("context.mmd", "level0.mmd", "level1.mmd"):
check_rules(path)
check_balance("context.mmd", "level0.mmd", "S")
check_balance("level0.mmd", "level1.mmd", "P1")
python3 dfd_check.py
実行結果です。U は利用者、A は備品管理者、S はコンテキスト図のプロセス、D1・D2 はデータストアの ID です。
[接続ルール] context.mmd
フロー 6 本、指摘 0 件
[接続ルール] level0.mmd
フロー 15 本、指摘 0 件
[接続ルール] level1.mmd
フロー 9 本、指摘 0 件
[階層の一致] context.mmd の S と level0.mmd
一致: 入力 A 備品情報
一致: 入力 U 貸出申請
一致: 入力 U 返却連絡
一致: 出力 A 貸出状況レポート
一致: 出力 U 貸出可否の通知
一致: 出力 U 返却期限のお知らせ
親 6 本、子 6 本 -> 一致
[階層の一致] level0.mmd の P1 と level1.mmd
一致: 入力 D1 在庫数
一致: 入力 U 貸出申請
一致: 出力 D1 在庫の引当
一致: 出力 D2 貸出内容
一致: 出力 U 貸出可否の通知
一致: 出力 U 返却期限のお知らせ
親 6 本、子 6 本 -> 一致
レベル1から「返却期限のお知らせ」の行を消した図で check_balance("level0.mmd", "level1_ng.mmd", "P1") を呼ぶと、欠けたフローが「親だけ」として表示されます。
[階層の一致] level0.mmd の P1 と level1_ng.mmd
一致: 入力 D1 在庫数
一致: 入力 U 貸出申請
一致: 出力 D1 在庫の引当
一致: 出力 D2 貸出内容
一致: 出力 U 貸出可否の通知
親だけ: 出力 U 返却期限のお知らせ
親 6 本、子 5 本 -> 不一致
接続ルールに合わない例と直し方
次の図は、3本の矢印すべてがプロセスを通っていません。
flowchart LR
U[利用者] -->|貸出申請| D2[(D2 貸出記録)]
D2 -->|貸出履歴| D1[(D1 備品台帳)]
U -->|連絡| A[備品管理者]
プロセスを間に入れると、誰がデータを動かすのかがはっきりします。
flowchart LR
U[利用者] -->|貸出申請| P1((1<br>貸出を受け付ける))
P1 -->|貸出内容| D2[(D2 貸出記録)]
D2 -->|貸出履歴| P4((4<br>貸出状況を集計する))
P4 -->|貸出状況レポート| A[備品管理者]
2つの図を先ほどのコードで調べた結果です。
python3 dfd_check.py ng.mmd ok.mmd
[接続ルール] ng.mmd
要修正: U -> D2(貸出申請)は 外部実体 と データストア の直結
要修正: D2 -> D1(貸出履歴)は データストア と データストア の直結
要修正: U -> A(連絡)は 外部実体 と 外部実体 の直結
フロー 3 本、指摘 3 件
[接続ルール] ok.mmd
フロー 4 本、指摘 0 件
そのほかに、書いたあとで見直したい点をまとめます。
| 見直す点 | こうなっていたら | 直し方 |
|---|---|---|
| 出力だけのプロセス | 入力が無いのにデータが生まれている | 元になる入力(外部実体かデータストアから)を足す |
| 入力だけのプロセス | 受け取ったデータがどこにも行かない | 出力先を足すか、プロセスが不要でないか見直す |
| 書き込みだけのデータストア | ためたデータを誰も読まない | 読むプロセスを足す。別の図で読むなら注記する |
| 読み出しだけのデータストア | データがいつ入るのかがわからない | 登録するプロセスを足す |
| プロセス名が「〜処理」「〜管理」 | 何をするのかが名前から読み取れない | 「貸出を受け付ける」のように「目的語+動詞」で書く |
| データフロー名が「データ」「情報」 | 中身がわからない | 「貸出申請」「在庫数」のように具体的な名詞にする |
| 1枚にプロセスが8個以上 | 矢印が交差して読みにくい | いくつかをまとめて1つにし、下の階層で分ける |
| 条件や順番を矢印で表している | フローチャートになっている | 条件はプロセスの中の話として、下の階層か処理の説明に書く |
DFD とフローチャートの違い
| 観点 | DFD | フローチャート |
|---|---|---|
| 書くもの | データの流れ | 処理の順序と条件 |
| 矢印の意味 | データが渡る | 次にこの処理へ進む |
| 矢印の名前 | データの名前(必須) | 基本は無し(分岐の「はい/いいえ」だけ) |
| 条件分岐・繰り返し | 書かない | ひし形で書く |
| 開始と終了 | 無い(データがあればいつでも動く) | ある |
| データをためる場所 | データストアとして書く | 書かないことが多い |
| 時間の順番 | 表さない(並行して動いてもよい) | 上から下へ順に進む |
| 向く用途 | システムの範囲、機能の分け方、機能とデータの関係を決める | 1つの処理の手順やロジックを決める |
同じ「貸出を受け付ける」を、フローチャートで書くと次のようになります。
flowchart TD
S([開始]) --> A[貸出申請を受け取る]
A --> B{申請内容に不備があるか}
B -->|ある| C[不可の通知を送る]
B -->|ない| D{在庫があるか}
D -->|ない| C
D -->|ある| E[在庫を引き当てる]
E --> F[貸出を記録する]
F --> G[可の通知と返却期限を送る]
C --> Z([終了])
G --> Z
レベル1の DFD と比べると、違いがはっきりします。
| 比べる点 | レベル1の DFD | フローチャート |
|---|---|---|
| 「在庫があるか」の判断 | 図に出てこない(プロセス1.2の中の話) | ひし形として出てくる |
| 備品台帳・貸出記録 | データストアとして出てくる | 出てこない |
| 利用者に届くもの | 「貸出可否の通知」「返却期限のお知らせ」と名前で書く | 「通知を送る」という処理として書く |
| 読み取れること | どの処理がどのデータを使うか | どの順で、どの条件で処理するか |
データの出どころと行き先を決めたいときは DFD、処理の中身の手順を決めたいときはフローチャートと使い分けます。DFD でプロセスを分け、その1つずつの中身をフローチャートで書く、という組み合わせ方もできます。
よくある質問
Q. DFD のレベルは、どこまで細かくすればよいですか? A. 1つのプロセスの中身を数行の文章で説明できるところまでが目安です。そこまで分けたら、プロセスの中身は文章やフローチャートで書きます。すべてのプロセスを同じ深さまで分ける必要はありません。
Q. デマルコ式とゲイン・サーソン式は、どちらを使えばよいですか? A. 表せる内容は同じです。チームや文書の標準があればそれに合わせ、無ければ使う作図ツールで書きやすいほうを選びます。プロセスの番号やデータストアの番号を図形の中に書きたい場合は、欄が分かれているゲイン・サーソン式が向いています。
Q. データストアはデータベースのテーブルと同じですか? A. 同じとは限りません。データストアは「データをためておく場所」という論理的な単位で、紙の台帳やファイルも含みます。設計が進んだ段階で、テーブルやファイルに対応づけます。
Q. Mermaid に DFD 専用の図はありますか? A. 今回使った Mermaid 11.14.0 では、flowchart の長方形・円・円柱を使い分けて書きました。記号の形は本来の DFD と少し異なるので、図の近くに凡例(長方形=外部実体、円=プロセス、円柱=データストア)を書いておくと親切です。
まとめ
- DFD はデータの流れだけを書く図。要素は外部実体・プロセス・データストア・データフローの4つ
- デマルコ式は円と平行線、ゲイン・サーソン式は角丸の長方形と片側が開いた長方形。表せる内容は同じ
- 矢印の両端のどちらかは必ずプロセス。外部実体どうし、データストアどうし、外部実体とデータストアは直接つながない
- コンテキスト図で範囲を決め、レベル0で主要な機能に分け、レベル1で1つずつ細かくする
- 親と子の図で入出力のデータフローが一致するかを、チェック表かコードで確かめる
- プロセスは「目的語+動詞」、データフローは具体的な名詞で名前を付ける
- 順序と条件を書くのがフローチャート、データの流れを書くのが DFD。目的で使い分ける
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「DFD(データフロー図)の書き方と記号|コンテキスト図・レベル0の例」
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。