はじめに
前回は、業務の現状分析から要求定義・要件定義までを行いました。
前回の記事:Python × Dify × RAGで学ぶ業務システム開発入門【第2回 要求定義・要件定義編】
第3回では、定義した機能要件をテーブル構造(ER図)と画面設計に落とし込みます。
コードを書き始める前にこの設計を固めることには、明確な理由があります。
ER図を先に設計する理由
→ テーブル構造が決まれば、どんなSQLを書けばよいかが見える
→ 後からカラムを追加すると、既存コードへの影響が大きい
画面設計を先に行う理由
→ 画面が決まれば、必要なURLとデータの流れが見える
→ 実装途中で「この画面、どこに置く?」という混乱を防げる
本連載は「作って終わり」ではなく、なぜそう設計するのかを重視しています。
設計の意図を理解することで、他のシステムにも応用できる力が身につきます。
本記事で学ぶこと
| 項目 | 内容 |
|---|---|
| ER図 | テーブル設計・カラム定義・リレーション |
| 画面一覧 | 必要な画面とURLの整理 |
| 画面遷移図 | 画面間の移動ルールの整理 |
| ワイヤーフレーム | 主要画面のレイアウト設計 |
ER図とは何か
ER図(Entity-Relationship Diagram)とは、データベースのテーブル構造と関係性を図で表したものです。
Entity(エンティティ) ── テーブルに対応する
Relationship(リレーション) ── テーブル間の関係(1対多・多対多)
Attribute(属性) ── テーブルのカラム
ER図を読み書きできるようになると、
- 「このデータはどのテーブルに入れるべきか」
- 「JOINはどのテーブルとどのテーブルで行うか」
- 「外部キーはどこに置くか」
といった判断が素早くできるようになります。
テーブル設計
第2回で定義した機能要件(F-01〜F-53)をもとに、必要なテーブルを設計します。
テーブル一覧
| テーブル名 | 概要 | 対応機能 |
|---|---|---|
user |
ログインユーザー・権限管理 | F-01〜F-03 |
department |
部署マスタ | F-15 |
employee |
社員マスタ | F-10〜F-16 |
training |
研修マスタ | F-20〜F-21 |
training_enrollment |
研修受講登録(誰がどの研修を受講するか) | F-22 |
training_history |
研修受講履歴(出席率・理解度・課題スコア) | F-23〜F-25 |
evaluation |
評価(スコア・AIコメント) | F-30〜F-33 |
daily_report |
日報・週報 | F-40〜F-42 |
各テーブルの詳細設計
user(ログインユーザー)
| カラム名 | 型 | 制約 | 説明 |
|---|---|---|---|
| id | INTEGER | PK / AUTO | 主キー |
| username | TEXT | NOT NULL / UNIQUE | ログインID |
| password | TEXT | NOT NULL | ハッシュ化済みパスワード |
| role | TEXT | NOT NULL | 権限(admin / staff / user) |
| created_at | DATETIME | DEFAULT NOW | 作成日時 |
password カラムには平文のパスワードを絶対に保存しないでください。
第4回で werkzeug.security.generate_password_hash を使ったハッシュ化を実装します。
department(部署マスタ)
| カラム名 | 型 | 制約 | 説明 |
|---|---|---|---|
| id | INTEGER | PK / AUTO | 主キー |
| dept_name | TEXT | NOT NULL / UNIQUE | 部署名 |
| created_at | DATETIME | DEFAULT NOW | 作成日時 |
employee(社員マスタ)
| カラム名 | 型 | 制約 | 説明 |
|---|---|---|---|
| id | INTEGER | PK / AUTO | 主キー |
| employee_no | TEXT | NOT NULL / UNIQUE | 社員番号 |
| name | TEXT | NOT NULL | 氏名 |
| department | TEXT | 部署名 | |
| position | TEXT | 役職 | |
| hire_date | DATE | 入社日 | |
| is_active | INTEGER | DEFAULT 1 | 有効フラグ(0=退職) |
| created_at | DATETIME | DEFAULT NOW | 作成日時 |
| updated_at | DATETIME | DEFAULT NOW | 更新日時 |
is_active = 0 にすることで論理削除を実現します。
物理削除(レコードを消す)と異なり、退職者のデータを研修履歴・評価に残せます。
training(研修マスタ)
| カラム名 | 型 | 制約 | 説明 |
|---|---|---|---|
| id | INTEGER | PK / AUTO | 主キー |
| training_name | TEXT | NOT NULL | 研修名 |
| start_date | DATE | 開始日 | |
| end_date | DATE | 終了日 | |
| description | TEXT | 研修概要 | |
| created_at | DATETIME | DEFAULT NOW | 作成日時 |
training_history(研修受講履歴)
| カラム名 | 型 | 制約 | 説明 |
|---|---|---|---|
| id | INTEGER | PK / AUTO | 主キー |
| employee_id | INTEGER | FK → employee.id | 受講者 |
| training_id | INTEGER | FK → training.id | 研修 |
| attendance_rate | REAL | DEFAULT 0.0 | 出席率(0.0〜100.0) |
| understanding_level | INTEGER | DEFAULT 0 | 理解度スコア(0〜100) |
| report_score | INTEGER | DEFAULT 0 | 課題・レポートスコア |
| created_at | DATETIME | DEFAULT NOW | 作成日時 |
employee_id と training_id の組み合わせで、誰がどの研修を受講したかを表します。
この2つの外部キーの組み合わせに UNIQUE 制約を設けることで、同じ組み合わせの重複登録を防ぎます。
evaluation(評価)
| カラム名 | 型 | 制約 | 説明 |
|---|---|---|---|
| id | INTEGER | PK / AUTO | 主キー |
| employee_id | INTEGER | FK → employee.id | 評価対象社員 |
| score | REAL | 総合評価スコア | |
| ai_comment | TEXT | AI生成コメント | |
| created_at | DATETIME | DEFAULT NOW | 評価日時 |
daily_report(日報)
| カラム名 | 型 | 制約 | 説明 |
|---|---|---|---|
| id | INTEGER | PK / AUTO | 主キー |
| employee_id | INTEGER | FK → employee.id | 日報作成者 |
| report_date | DATE | NOT NULL | 日報の日付 |
| content | TEXT | 学習内容・作業内容 | |
| memo | TEXT | 所感・気づき | |
| created_at | DATETIME | DEFAULT NOW | 作成日時 |
ER図(全体)
テーブル間のリレーションを整理します。
┌──────────┐ ┌─────────────────────┐ ┌──────────┐
│ employee │ 1 * │ training_history │ * 1 │ training │
│──────────│─────────│─────────────────────│─────────│──────────│
│ id PK │ │ id PK │ │ id PK │
│ emp_no │ │ employee_id FK(*) │ │ name │
│ name │ │ training_id FK(*) │ │ start_dt │
│ dept │ │ attendance_rate │ │ end_dt │
│ position │ │ understanding_level │ └──────────┘
│ hire_date│ │ report_score │
│ is_active│ └─────────────────────┘
└────┬─────┘
│ 1
│ ┌────────────┐
│ * │ evaluation │
└────────────────│────────────│
│ │ id PK │
│ │ employee_id│
│ │ score │
│ │ ai_comment │
│ └────────────┘
│ 1
│ ┌──────────────┐
│ * │ daily_report │
└────────────────│──────────────│
│ id PK │
│ employee_id │
│ report_date │
│ content │
└──────────────┘
┌──────┐ ┌──────┐
│ user │ │ dept │
│──────│ │──────│
│ id │ │ id │
│ name │ │ name │
│ role │ └──────┘
└──────┘
リレーションの読み方
| 関係 | 説明 |
|---|---|
| employee 1 → * training_history | 1人の社員が複数の研修を受講できる |
| training 1 → * training_history | 1つの研修に複数の社員が参加できる |
| employee 1 → * evaluation | 1人の社員が複数回評価される(年度ごとなど) |
| employee 1 → * daily_report | 1人の社員が複数の日報を持つ |
画面設計
テーブル設計が終わったら、次は画面設計です。
「どんな画面が必要か」「画面間をどう移動するか」を先に決めます。
画面一覧
| 画面名 | URL | HTTPメソッド | 対応機能 | 権限 |
|---|---|---|---|---|
| ログイン | /login |
GET / POST | F-01 | 全員 |
| ダッシュボード | / |
GET | - | 全員 |
| 社員一覧 | /employees |
GET | F-10 / F-11 | staff / admin |
| 社員登録 | /employees/new |
GET / POST | F-12 | staff / admin |
| 社員編集 | /employees/<id>/edit |
GET / POST | F-13 | staff / admin |
| 社員削除 | /employees/<id>/delete |
POST | F-14 | admin |
| 研修一覧 | /trainings |
GET | F-20 | staff / admin |
| 研修登録 | /trainings/new |
GET / POST | F-21 | staff / admin |
| 研修履歴 | /trainings/<id>/history |
GET / POST | F-22〜F-25 | staff / admin |
| 評価一覧 | /evaluations |
GET | F-30 | staff / admin |
| 評価コメント生成 | /evaluations/<id>/generate |
POST | F-31 | staff / admin |
| Excel取込 | /import |
GET / POST | F-16 / F-26 | staff / admin |
| Excel出力 | /export |
GET | F-32 | staff / admin |
| 日報一覧 | /reports |
GET | F-41 | 全員 |
| 日報登録 | /reports/new |
GET / POST | F-40 | user以上 |
| 社内文書検索 | /search |
GET / POST | F-50 / F-51 | 全員 |
| FAQチャット | /chat |
GET | F-52 / F-53 | 全員 |
画面遷移図
画面間の移動ルールを整理します。
[ログイン画面]
│ ログイン成功
▼
[ダッシュボード] ─────────────────────────────────────┐
│ │
├──▶ [社員一覧] │
│ ├──▶ [社員登録] ──▶ 登録完了 ──▶ [社員一覧]
│ └──▶ [社員編集] ──▶ 更新完了 ──▶ [社員一覧]
│
├──▶ [研修一覧]
│ └──▶ [研修履歴(出席・理解度入力)]
│ └──▶ 保存完了 ──▶ [研修履歴]
│
├──▶ [評価一覧]
│ └──▶ [AIコメント生成] ──▶ 生成完了 ──▶ [評価一覧]
│
├──▶ [Excel取込] ──▶ 取込完了 ──▶ [社員一覧 or 研修履歴]
├──▶ [Excel出力] ──▶ ファイルダウンロード
│
├──▶ [日報一覧]
│ └──▶ [日報登録] ──▶ 登録完了 ──▶ [日報一覧]
│
├──▶ [社内文書検索] ──▶ 検索結果表示(同一画面)
└──▶ [FAQチャット] ──▶ チャット応答(同一画面)
│
ログアウト ◀──────────────────┘
│
[ログイン画面]
ワイヤーフレーム(主要画面)
実際の開発では Figma などのツールを使います。
ダッシュボード
社員一覧
社員登録・編集フォーム
評価一覧・AIコメント生成
社内文書検索(RAG)
設計をコードに反映する
ER図と画面設計が決まれば、実装の見通しが立ちます。
設計と実装の対応関係を整理しておきます。
テーブル設計 → database.py
# database.py(抜粋)
def init_db():
conn = get_db()
cur = conn.cursor()
cur.executescript("""
CREATE TABLE IF NOT EXISTS employee (
id INTEGER PRIMARY KEY AUTOINCREMENT,
employee_no TEXT NOT NULL UNIQUE,
name TEXT NOT NULL,
department TEXT,
position TEXT,
hire_date DATE,
is_active INTEGER NOT NULL DEFAULT 1, -- 論理削除フラグ
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS training_history (
id INTEGER PRIMARY KEY AUTOINCREMENT,
employee_id INTEGER NOT NULL REFERENCES employee(id),
training_id INTEGER NOT NULL REFERENCES training(id),
attendance_rate REAL DEFAULT 0.0,
understanding_level INTEGER DEFAULT 0,
report_score INTEGER DEFAULT 0,
-- 同一社員が同一研修に重複登録されるのを防ぐ
UNIQUE (employee_id, training_id)
);
""")
conn.commit()
画面設計 → app.py のルーティング
# app.py(抜粋)
# 画面一覧の URL がそのままルーティングに対応する
@app.route("/employees") # 社員一覧
@app.route("/employees/new") # 社員登録
@app.route("/employees/<id>/edit") # 社員編集
@app.route("/employees/<id>/delete") # 社員削除(POST)
@app.route("/trainings") # 研修一覧
@app.route("/trainings/<id>/history") # 研修履歴
@app.route("/evaluations") # 評価一覧
@app.route("/evaluations/<id>/generate") # AIコメント生成(POST)
@app.route("/search") # 社内文書検索
@app.route("/chat") # FAQチャット
画面設計 → templates/ のHTMLファイル
templates/
├── base.html ← 共通ナビゲーション(全画面で継承)
├── login.html ← ログイン画面
├── index.html ← ダッシュボード
├── employee_list.html ← 社員一覧ワイヤーフレームに対応
├── employee_form.html ← 社員登録・編集フォームに対応
├── training_list.html ← 研修一覧
├── training_history.html ← 研修履歴(出席・理解度入力)
├── evaluation.html ← 評価一覧・AIコメント生成に対応
├── import.html ← Excel取込
├── report.html ← 日報一覧
├── search.html ← 社内文書検索ワイヤーフレームに対応
└── chat.html ← FAQチャット
今後の連載予定
| 回 | タイトル | 主な内容 |
|---|---|---|
| 第1回 | 業務システム全体設計編 | システム概要・技術選定・アーキテクチャ |
| 第2回 | 要求定義・要件定義編 | 業務分析・機能要件・非機能要件 |
| 第3回 | ER図・画面設計編(本記事) | テーブル設計・画面遷移図・ワイヤーフレーム |
| 第4回 | Flaskログイン機能編 | セッション・パスワードハッシュ・認証ミドルウェア |
| 第5回 | 社員管理CRUD編 | 一覧・登録・更新・削除・バリデーション |
| 第6回 | 研修管理編 | リレーション・集計・出席率自動計算 |
| 第7回 | Excel業務自動化編 | pandas取込・openpyxl出力・テンプレート活用 |
| 第8回 | Dify API連携編 | プロンプト設計・API呼び出し・エラーハンドリング |
| 第9回 | RAG構築編 | Knowledgeへの登録・Embedding・検索精度改善 |
| 第10回 | FAQチャットボット編 | チャットUI・会話履歴・ストリーミングレスポンス |
| 第11回 | GitHubチーム開発編 | ブランチ戦略・Pull Request・コードレビュー |
| 第12回 | テスト編 | pytest・テスト設計・デモ・振り返り |
おわりに
第3回では、ER図と画面設計を通じて「実装の地図」を作成しました。
ポイントを振り返ります。
- ER図:8つのテーブルとリレーションを設計。外部キー・論理削除・UNIQUE制約の考え方を整理した
-
画面一覧:17画面のURL・HTTPメソッド・権限を整理し、
app.pyのルーティングと対応させた - 画面遷移図:画面間の移動ルールを可視化し、実装時の迷いをなくした
- ワイヤーフレーム:主要5画面のレイアウトを設計し、テンプレートHTML作成の土台を作った
次回は**「Flaskログイン機能編」**として、ログイン・ログアウト・セッション管理・パスワードハッシュ化を実装します。
設計した user テーブルと login 画面のワイヤーフレームを使って、いよいよコードを書き始めます。
次回 : Python × Dify × RAGで学ぶ業務システム開発入門【第4回 Flaskログイン機能編】




