線で結ばれた二つの箱でデータのつながりを表すER図を表した表紙画像
FUJIKAWA LAB SPECIMEN 49 / 49
fujikawa.com の設計図

このサイトの仕組みを、
ER図で読む。

fujikawa.com はデータベースを使っていません。記事や教材の情報は、JSON ファイルと Python の設定に書かれています。 それでも、データの種類とつながりを箱と線で描くと、どこを直せば何が変わるかが一目で分かります。 このページでは、このサイト自身を例に、ER図の読み方と描き方を説明します。

WHAT IS AN ER DIAGRAM

ER図とは

ER図(実体関連図)とは、システムが扱うデータの種類と、それらのつながりを箱と線で表した図です。 E は Entity(エンティティ=データの種類)、R は Relationship(リレーションシップ=つながり)の頭文字です。 箱の中にはそのデータが持つ項目を書き、線の両端には「相手が何個つながるか」を記号で書きます。

ER図はふつう、データベースの設計に使われます。しかし、データベースがなくても描けます。 このサイトでは、記事の一覧は posts.json、教材の一覧は data/lab_catalog.json、公開するページの一覧は site_meta.py という、ファイルそのものが「表」の役目をしています。 表同士は、記事の slug やページの path(URL)といった共通の値でつながっています。

📝
このページの作り方と時点

2026年9月18日に、このサイトのソースコード(公開していないリポジトリ)を AI(Claude Code)に読ませて図を作り、件数はファイルを数えて確かめました。 件数はこのページを追加した時点のもので、記事や教材が増えると変わります。

01STEP

図の読み方

覚えることは3つです。箱の見出し、箱の中の鍵、線の端の記号です。

箱の見出し

1行目がデータの名前、2行目がその実物の置き場所と件数です。例えば「記事 / posts.json / 21件」は、記事の情報が posts.json に21件書かれているという意味です。

PK と FK

  • PK(主キー):その箱の中で1件を見分けるための値です。記事なら slug(URL の末尾の文字列)が PK で、同じ slug の記事は2本存在できません。
  • FK(外部キー):別の箱の PK を指している値です。記事の content_file は、本文ファイルの名前を指しています。

線の端の記号

線の端の記号は、その端につながる箱が「何個つながるか」を表します。この書き方はカラスの足に似ていることから「カラスの足記法」と呼ばれます。

線の端の記号の読み方 線の端の記号は、相手側から見て何個つながるかを表す。縦線2本はちょうど1つ、丸と縦線は0か1つ、縦線と三叉は1つ以上、丸と三叉は0以上。 ちょうど1つ 記事と本文(1本の記事に本文は1つ) 0か1つ 教材と確認記録(ツールは持たない) 1つ以上 確認記録と出典 0以上(多) タグ(1本の記事に何個でも)
記号は、箱に接する側に描きます。三叉(みつまた)が「多」、丸が「0でもよい」、縦線が「1」を表します。
02STEP

全体の見取り図

ER図に入る前に、ページが表示されるまでの流れを見ておきます。これは ER図ではなく、処理の順番を表した図です。

リクエストの流れ ブラウザからのリクエストはVercelが振り分け、決まったファイルはstaticから直接返し、それ以外はFlaskアプリが記事・台帳・テンプレートを読み込んでページを組み立てる。 読者のブラウザ URL を開く Vercel vercel.json の規則で 行き先を振り分ける static/ のファイル sitemap.xml / llms.txt 画像 / フォント / CSS をそのまま返す Flask アプリ(app.py) URL に合うページを その場で組み立てる 記事 posts.json content/*.html ページの登録と台帳 site_meta.py lab_catalog.json テンプレート templates/*.html _partials その他のデータ x-following.json 決まったファイル それ以外すべて 読み込む
矢印は処理の向きです。

このサイトは Vercel(ウェブサイトを置いて配信するサービス)で動いています。 読者が URL を開くと、Vercel は設定ファイル vercel.json の規則を見て、行き先を2つに振り分けます。

  • 決まったファイルsitemap.xmlllms.txt、画像、フォントなど):static/ フォルダにあるファイルを、そのまま返します。
  • それ以外Flask(Python でウェブアプリを作るための道具)で書いたアプリ app.py に渡します。アプリは記事や台帳のデータを読み、テンプレート(HTML の型)に流し込んでページを組み立てます。

以降の ER図は、この図の下段にある「データ」の中身を描いたものです。

03STEP

記事のデータ

ブログ記事(/blog)に関わるデータは5種類です。中心は posts.json です。

記事まわりのER図 記事(posts.json)は本文(content)と1対1、タグとは多対多、表紙画像と1対1。旧URLの転送は記事を1件指す。 記事 posts.json / 21件 PK slug(URL の末尾) title date / updated FK content_file tags excerpt / key_points og_cover_title(表紙の文字) 記事の本文 content/*.html / 21件 PK content_file 本文の HTML タグ posts.json の tags から導出 / 8種 PK name slug(URL 用に変換) 記事の表紙画像 static/images/blog-og / 21枚 PK slug 材料の指紋(manifest.json) 旧URLの転送 app.py の BLOG_REDIRECTS / 1件 PK old_slug FK slug(転送先) 本文を1つ持つ 多対多 表紙を生成 古い slug を転送
網掛けの見出しが中心のデータです。

記事と本文は、別のファイルに分かれている

posts.json には、タイトル・日付・タグ・要約などの「記事についての情報」だけが入っています。本文の HTML は content/ フォルダに1記事1ファイルで置き、content_file の値でつなぎます。 この分け方により、一覧ページやタグのページは本文を読まずに作れます。

タグは、記事から作られる

タグには専用のファイルがありません。アプリが全記事の tags を集めて、その場でタグの一覧を作ります。URL 用の文字列(slug)も、タグ名から決まった規則で変換します。 1本の記事は複数のタグを持ち、1つのタグは複数の記事に付くので、記事とタグは「多対多」です。関連記事は、タグが1つでも共通する記事を新しい順に3本選んでいます。

表紙画像は、記事の情報から生成する

SNS で共有したときに表示される表紙画像(OG 画像)は、og_cover_title と最初のタグから、生成用のプログラムが作ります。 画像ごとに材料の指紋(ハッシュ値)を記録しておき、記事の情報が変わったのに画像を作り直していない状態を検査で見つけます。

URL を変えた記事は、転送表で古い URL を残す

記事を統合して URL が変わったときは、古い slug と新しい slug の対応をアプリの中に書いておきます。古い URL を開いた人は、新しい記事へ自動で転送されます。

04STEP

教材(LAB)のデータ

このページのような教材やツールは「FUJIKAWA LAB」の作品として管理しています。記事より箱の数が多く、つながりも複雑です。

教材(FUJIKAWA LAB)まわりのER図 教材台帳の1件は公開ページの登録と1対1で、章ページを0件以上持つ。確認記録は0か1件で出典を1件以上持つ。表紙の文言から表紙画像を生成する。公開ページはテンプレートで描画され、テンプレートは共通部品を読み込む。LABのページは検索用の設定を1件持つ。 確認記録 lab_catalog.json の reading / 45件 environment(対象環境) reviewed_on(照合日) review_basis(照合時のみ) 出典 reading の sources label url(公式情報) 教材(LAB 作品) data/lab_catalog.json / 49件 PK number(不変の標本番号) FK path title / description category / kind topics related_prefixes(章) reading 表紙の文言 FUJIKAWA_LAB_IMAGE_COPY / 49件 PK path 見出し 図柄の種類 代替テキスト 教材の表紙画像 static/images/lab-og / 49枚 PK path 公開ページの登録 site_meta.py の STATIC_PAGES / 79件 PK path(URL) endpoint FK template sitemap の優先度・頻度 lastmod(最終更新日) テンプレート templates/*.html / 89件 PK file 共通部品 templates/_partials / 14件 PK file 検索用の設定 FUJIKAWA_LAB_PAGE_SEO / 67件 PK path title description schema_type 1対1 章ページ 0か1 1つ以上 表紙の文言 画像を生成 描画に使う 読み込む(多対多) LABのページだけ持つ
中央の列が教材そのもの、右の列がページとして公開するための情報、左の列が内容の確認記録です。

「公開する登録」と「作品としての台帳」は別

site_meta.pySTATIC_PAGES は、URL とテンプレートの対応を並べた「公開する登録」です。ここに書いたページが URL として登録され、サイトマップにも載ります。 一方 data/lab_catalog.json は、作品としてのタイトル・説明・種類・確認記録を並べた「台帳」です。 台帳の1件は、必ず登録の1件と path で対応します。登録にはあっても台帳にないページ(章ページや /start など)もあります。

作品番号は変えない

台帳の PK は number(標本番号)です。一度付けた番号は、内容を大きく書き直しても変えません。各ページには「SPECIMEN 番号 / 総数」の形で表示されます。

章に分かれた教材

複数の章ページから成る教材は、related_prefixes に URL の先頭部分を書いて、章ページを親の作品に結び付けます。章ページは、親の対象環境と確認記録を引き継ぎます。

確認記録と出典

教材には、対象とする環境と、公式情報と照合した日を記録します。照合していない教材は日付を空にしておき、ページには「未記録」と表示します。ツールやゲームは確認記録を持たないので、この線は「0か1つ」です。

検索用の設定は、LAB のページが持つ

LAB の作品ページと章ページ(合わせて67ページ)は、検索結果や SNS に出すタイトルと説明文を FUJIKAWA_LAB_PAGE_SEO に登録します。 表紙画像の URL と、画像の代わりに読み上げる説明文(代替テキスト)は、親の作品の「表紙の文言」から自動で補われます。LAB 以外のページ(/start など)は、この設定を持ちません。

05STEP

生成して置くファイル

ER図の箱には、人が書くデータと、プログラムがデータから作るファイルがあります。作るファイルは、作り直すのを忘れると古いまま公開されます。

次の表は、元のデータから作られるファイルと、作られる時点をまとめたものです。「手元で生成」は、手元でコマンドを実行して作ったファイルを、変更として記録してから公開します。「表示のたび」は、読者が開くたびにアプリが作るので、作り直しの手間はありません。

作られるもの元のデータ作る道具作られる時点
サイトマップ(sitemap.xml記事・公開ページの登録・タグgenerate_sitemap.py手元で生成
AI 向けの案内(llms.txt記事と本文・教材の台帳・検索用の設定generate_llms_txt.py手元で生成
フォントの部分集合記事・テンプレート・台帳で使う文字generate_fonts.py手元で生成
記事の表紙画像記事の表紙の文字と最初のタグgenerate_og_images.py手元で生成
教材の表紙画像表紙の文言・作品番号と総数generate_lab_og_images.py手元で生成
RSS フィード(/feed.xml記事と本文app.py表示のたび
記事一覧・タグのページ記事app.py表示のたび
💡
フォントを部分集合にする理由

日本語のフォントは、全部の文字を含めると1ファイルが数 MB になります。このサイトでは実際に使っている文字だけを切り出して、読み込みを軽くしています。その代わり、新しい文字を含むページを足したら作り直しが必要です。

06STEP

1件足すと動くもの

ER図の使い道の1つは、変更の影響範囲を先に数えられることです。線をたどると、1件の追加で触るファイルが分かります。

作業記事を1本足す教材を1件足す
人が書くposts.json に1件、content/ に本文1ファイルテンプレート1ファイル、STATIC_PAGES と台帳に1件ずつ、表紙の文言と図柄、検索用の設定
作り直すサイトマップ、llms.txt、新しい文字があればフォントサイトマップ、llms.txt、フォント
表紙画像全記事分を作り直す全作品分を作り直す
手で合わせるなし表紙の図柄を描く関数、テストが期待する総数、フォントの指紋(SHA-256)
自動で変わる記事一覧、タグのページ、RSS、関連記事トップと LAB の作品一覧

1件足すと、表紙画像が全部変わる理由

記事の表紙には、新しい順で何番目かを示す番号が入っています。新しい記事が1本入ると、既存の記事はすべて1つずつ後ろにずれるので、全部の表紙を作り直すことになります。 教材の表紙には「SPECIMEN 番号 / 総数」が入っているため、1件足すと総数が変わり、やはり全部の表紙が変わります。

ER図で言えば、表紙画像の箱は1件のデータだけでなく「全体の件数」にもつながっている、ということです。図に描いてある線だけでなく、こうした隠れたつながりを見つけて書き添えておくと、図の価値が上がります。

07STEP

壊れないための検査

データベースには、つながりが壊れないように守る仕組みがあります。ファイルで管理するこのサイトでは、その役目を自動テストが担っています。

データベースは、存在しない相手を指す FK を登録しようとすると拒否します。JSON ファイルにはその仕組みがないので、このサイトでは、変更を公開する前に自動テスト(プログラムの正しさを確かめるプログラム)を実行して、つながりの食い違いを見つけています。テストのファイルは21本あり、例えば次のことを確かめます。

  • 台帳の作品番号が 1 から総数まで欠けなく並び、各作品が公開ページの登録にちょうど1回ずつあること
  • 各教材ページに正しい「SPECIMEN 番号 / 総数」が表示され、表紙画像が作品数と同じ枚数あること
  • 記事の表紙画像が、今の記事の情報から作られたものであること
  • ページが読み込む外部のファイルが、サイトの安全設定(CSP:読み込んでよい外部の場所を限定する設定)で許可されていること
  • すべての公開ページが表示でき、エラーにならないこと

変更を GitHub に送ると、GitHub 上でも同じテストが実行されます。ただし、このテストは公開を止める関門ではありません。Vercel は同時に新しい版を公開するので、公開と並行して行う確認という位置づけです。そのため、手元でテストを通してから送ることを決まりにしています。

テストが確かめる「総数」は、テストの中に数字で書いてあります。教材を1件足したら、この数字も手で1つ増やします。数字が合っていないとテストが失敗するので、足し忘れや登録漏れに気づけます。

YOUR TURN

自分のサイトで描く

自分のサイトやツールでも、同じ手順で ER図を描けます。データベースを使っていなくてもかまいません。

👉
やってみよう! TRY IT — 01
  1. 表を探す:同じ形の情報が並んでいるファイルや一覧を探します。JSON、CSV、スプレッドシートのシート、設定ファイルの中の一覧などです。
  2. PK を決める:各一覧で、1件を見分ける値を1つ選びます。ID、URL、ファイル名などです。
  3. FK を探す:別の一覧の PK と同じ値を持っている項目を探します。それが線になります。
  4. 線の端を決める:「相手は必ずいるか」「相手は1つか複数か」を両側から確かめて、記号を付けます。
  5. 作られるものを足す:データから自動で作られるファイルも箱として描き、「作り直しが要るか」を書き添えます。

AI に頼む場合は、ソースコードを読ませたうえで「データの置き場所ごとに箱を作り、各箱の主キーとほかの箱を指す項目を挙げて、Mermaid の erDiagram で書いて」と指示すると、下書きが得られます。Mermaid は、文字で書いた図の指定から図を描く道具です。件数やつながりは、実際のファイルで確かめてください。

描けたかどうかのチェックリスト

GLOSSARY

用語集

ER図
データの種類(エンティティ)と、そのつながり(リレーションシップ)を箱と線で表した図。
エンティティ
図の箱1つ。記事、タグ、教材など、同じ形の情報の集まり。
主キー(PK)
箱の中で1件を見分けるための値。重複してはいけない。
外部キー(FK)
別の箱の主キーを指す値。線の元になる。
多対多
両側とも複数がつながる関係。記事とタグがこれに当たる。
JSON
データを文字で書くための決まった形式。{ }[ ] で項目と一覧を表す。
テンプレート
HTML の型。空欄にデータを流し込んでページを作る。
サイトマップ
サイト内のページの URL を並べたファイル。検索エンジンがページを見つけるのに使う。
llms.txt
AI 向けに、サイトの概要と主なページを文章でまとめたファイル。
OG 画像
SNS などでリンクを共有したときに表示される画像。
RESOURCES

参考にした資料

📚 あわせて読みたい教材

Textbook
Cloudflare と Workers を基礎から実践まで
小さなWebサービスを作って公開するまでを、データの保存先の選び方も含めて順に説明します。
Explainer
エージェントハーネスとは何か
AI に作業を任せるときの、道具・ルール・検査の組み立て方を説明します。