機能詳細設計書とは

基本設計が「何を作るか」を定義するのに対し、機能詳細設計書は「どう動くか」を定義するドキュメントだ。 実装者がこのドキュメントだけを見てコードを書けるレベルの精度が求められる。

機能詳細設計書が不十分だと、実装者がロジックを独自解釈して仕様齟齬が発生する。 特に条件分岐・例外処理・排他制御の記述が曖昧なプロジェクトは、後工程のテストで大量のバグが発生しやすい。

💡 基本設計との違い

基本設計の機能一覧・画面一覧が「機能の存在とインターフェース」を定義するのに対し、機能詳細設計書は「その機能の内部処理ロジック」を定義する。基本設計書の機能IDと1対1で対応させるのが原則。

定義すべき項目一覧

機能詳細設計書に必ず記載すべき項目を以下に示す。

分類定義項目必須度説明
基本情報機能ID / 機能名基本設計の機能一覧と紐付ける識別子
基本情報対象画面 / APIこの機能が紐づく画面・エンドポイント
基本情報処理概要3〜5行で何をする機能かを説明
基本情報作成者 / 更新日バージョン管理の起点
前提条件事前条件(Pre-condition)この処理が実行される前提となる状態
前提条件事後条件(Post-condition)処理が正常終了した後の状態
処理フロー主フロー(Happy Path)正常系の処理手順(番号付き)
処理フロー代替フロー(Alternative)条件分岐で生じる別経路
処理フロー例外フロー(Exception)エラー・異常発生時の処理
業務ロジック計算式・変換ロジック金額計算・コード変換など数式や変換ルール
業務ロジックバリデーションルール入力値の検証条件(型・桁数・必須・相関)
業務ロジックコード値の意味ステータスコード・区分コードの意味定義
DB操作参照テーブル / 更新テーブルCRUD操作の対象テーブルを全件列挙
DB操作WHERE条件 / JOIN条件複雑なクエリは条件を明示
排他制御楽観ロック / 悲観ロック同時更新制御の方式
非機能パフォーマンス要件応答時間・件数上限など
非機能ログ出力要件出力するログレベル・項目

処理フローの記述方法

処理フローは番号付きのステップで記述する。条件分岐が生じる箇所は「ステップ番号.条件」形式でインデントして記述するのが一般的だ。

以下は注文確定機能の処理フロー記述例だ。

処理フロー記述例(注文確定機能)
【主フロー】
1. リクエストのJWTトークンを検証する
   1a. 検証失敗 → 例外フロー[E01]へ
2. 注文IDが存在するかを orders テーブルで確認する
   2a. 存在しない → 例外フロー[E02]へ
3. 注文ステータスが「確認待ち(STATUS=10)」であることを確認する
   3a. STATUS≠10 → 例外フロー[E03]へ
4. 在庫テーブル(inventories)から各明細の在庫数を取得し、
   注文数量と照合する
   4a. 在庫不足の明細が1件でも存在する → 例外フロー[E04]へ
5. トランザクション開始
6. inventories.stock を注文数量分デクリメントする
7. orders.status を「確定済み(STATUS=20)」に更新する
8. order_histories に処理ログを INSERT する
9. コミット
10. 確定完了メール送信キューに enqueue する
11. レスポンス(200 OK, 確定済み注文情報)を返す

【例外フロー】
E01: 401 Unauthorized を返す。ログ出力:WARN
E02: 404 Not Found を返す。ログ出力:INFO
E03: 409 Conflict("注文はすでに処理済みです")を返す。ログ出力:WARN
E04: 409 Conflict("在庫不足:商品ID={id} 不足数={n}")を返す。
     ロールバック。ログ出力:WARN

フロー記述のポイント

① ステップは動詞で始める(「確認する」「取得する」「更新する」)。② 条件分岐は必ずインデントして例外フローと紐付ける。③ DBのテーブル名・カラム名・コード値は実際の物理名で記述する。実装者が設計書と実装を1:1で対応できる粒度が理想だ。

業務ロジックの定義

計算式・変換ルール・ステータス遷移など、実装者が判断を委ねてはならないロジックはすべて明文化する。 特に金額計算・税率・丸め処理は実装者によって解釈が異なりやすいため、必ず式で記述する。

業務ロジック定義例
■ 請求金額計算ロジック

  小計  = Σ(単価 × 数量)   ※小数点以下は切り捨て(FLOOR)
  割引額 = 小計 × 割引率      ※割引率は members.discount_rate(小数:0.00〜0.30)
              ※小数点以下は切り捨て(FLOOR)
  税込前 = 小計 - 割引額
  消費税 = 税込前 × 0.10      ※小数点以下は切り捨て(FLOOR)
  合計   = 税込前 + 消費税

■ ステータス遷移図

  10(確認待ち)
    → [注文確定API] → 20(確定済み)
    → [注文キャンセルAPI] → 90(キャンセル)
  20(確定済み)
    → [出荷登録API] → 30(出荷済み)
    → [注文キャンセルAPI] → ※不可(E03)
  30(出荷済み)
    → [配達完了Webhook] → 40(完了)

■ コード値定義(orders.status)

  10: 確認待ち(初期値。注文作成時に自動設定)
  20: 確定済み(在庫確保済み)
  30: 出荷済み(倉庫で出荷処理完了)
  40: 完了(配達完了確認済み)
  90: キャンセル(確認待ち状態でのみ遷移可)

例外・エラー処理の定義

例外処理の記述が甘いと、テスト工程で異常系のバグが大量発生する。以下の観点を漏らさず定義すること。

観点定義すべき内容
入力エラーバリデーション違反時のレスポンスコード・メッセージID・ログレベル
業務エラー在庫不足・残高不足など業務上の制約違反時の処理
システムエラーDB接続エラー・タイムアウト時のリトライ回数・フォールバック処理
外部サービスエラー外部API障害時の振る舞い(代替処理・キューイング・サーキットブレーカー)
ロールバックトランザクション中断時にロールバックが必要な操作の列挙
通知アラート・メール通知が必要なエラーの条件と宛先

⚠️ よくある記述漏れ

「エラー時は適切に処理する」「例外が発生した場合はエラーを返す」のような抽象的な記述では設計書として機能しない。エラーコード・HTTPステータス・ログレベル・リトライ回数を具体的に記述すること。

排他制御・トランザクション

複数ユーザーが同一データを操作する機能では、排他制御の方式を必ず定義する。 方式を選択しないと、実装者がそれぞれ独自の制御を行い不整合が発生する。

方式仕組み採用場面実装例
楽観ロック更新時にバージョン番号を照合。不一致なら競合エラー競合頻度が低い・ユーザー操作待ちが長いWHERE id=? AND version=?
悲観ロック取得時にDBロックを取得。他からの更新をブロック競合頻度が高い・バッチ処理SELECT ... FOR UPDATE
べき等設計同一リクエストを複数回送っても結果が変わらない設計API・非同期処理の重複実行対策冪等キー(idempotency_key)管理

Python Tips — ソースから処理フローを抽出

既存システムをリプレイスする場合、旧システムのソースコードから処理フローを逆引きするケースがある。 Pythonのastモジュールを使うと、Pythonコードの関数定義・呼び出し関係を自動で抽出できる。

Python — 関数呼び出し関係の抽出(astモジュール)
"""
既存Pythonソースから関数定義・呼び出し関係を抽出して
処理フロー把握の補助資料を生成するスクリプト
"""
import ast
import os
from pathlib import Path
from collections import defaultdict

def extract_function_calls(filepath: str) -> dict:
    """Pythonファイルから関数定義と内部呼び出しを抽出"""
    with open(filepath, encoding="utf-8") as f:
        source = f.read()

    try:
        tree = ast.parse(source)
    except SyntaxError as e:
        print(f"構文エラー: {filepath} — {e}")
        return {}

    result = {}

    for node in ast.walk(tree):
        if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
            func_name = node.name
            calls = []
            for child in ast.walk(node):
                if isinstance(child, ast.Call):
                    if isinstance(child.func, ast.Name):
                        calls.append(child.func.id)
                    elif isinstance(child.func, ast.Attribute):
                        calls.append(f"{child.func.attr}")
            result[func_name] = {
                "line": node.lineno,
                "calls": list(set(calls)),
                "is_async": isinstance(node, ast.AsyncFunctionDef),
            }

    return result


def scan_directory(target_dir: str, pattern: str = "*.py") -> dict:
    """ディレクトリ配下の全Pythonファイルをスキャン"""
    all_funcs = defaultdict(dict)
    for path in Path(target_dir).rglob(pattern):
        rel = str(path.relative_to(target_dir))
        funcs = extract_function_calls(str(path))
        if funcs:
            all_funcs[rel] = funcs
    return dict(all_funcs)


def print_call_tree(scan_result: dict) -> None:
    """処理フロー一覧を標準出力"""
    for filepath, funcs in scan_result.items():
        print(f"\n📄 {filepath}")
        for fname, info in funcs.items():
            prefix = "async " if info["is_async"] else ""
            print(f"  {'⚡' if info['is_async'] else '🔹'} {prefix}{fname}()  [L{info['line']}]")
            for call in info["calls"]:
                print(f"      └─ calls: {call}()")


# ── 使用例 ────────────────────────────────────────────────
if __name__ == "__main__":
    TARGET = "./src"   # 解析対象ディレクトリ
    result = scan_directory(TARGET)
    print_call_tree(result)

    # CSVに出力して設計書作成の補助資料にする
    import csv
    with open("function_flow.csv", "w", newline="", encoding="utf-8") as f:
        writer = csv.writer(f)
        writer.writerow(["ファイル", "関数名", "非同期", "行番号", "呼び出し先"])
        for fpath, funcs in result.items():
            for fname, info in funcs.items():
                writer.writerow([
                    fpath, fname,
                    "YES" if info["is_async"] else "NO",
                    info["line"],
                    " / ".join(info["calls"])
                ])

このスクリプトを実行すると function_flow.csv が生成され、 どの関数がどの関数を呼び出しているかが一覧化される。 ただし動的なメソッド呼び出し(getattr 経由など)は検出できないため、静的解析の補助資料として使うこと。

Python — JavaソースのメソッドコールをDB情報と紐付けて処理フロー推定
"""
Javaソースコードから「メソッド名 → SQL操作対象テーブル」の
マッピングを抽出するスクリプト(正規表現ベース)
既存Javaシステムのリプレイス時に処理フローの草稿を作る補助に使う
"""
import re
from pathlib import Path

# SQL操作を検出する正規表現
SQL_PATTERNS = {
    "SELECT": re.compile(r"(?i)\bFROM\s+([`\"\[]?[\w]+[`\"\]]?)", re.MULTILINE),
    "INSERT": re.compile(r"(?i)\bINSERT\s+INTO\s+([`\"\[]?[\w]+[`\"\]]?)", re.MULTILINE),
    "UPDATE": re.compile(r"(?i)\bUPDATE\s+([`\"\[]?[\w]+[`\"\]]?)", re.MULTILINE),
    "DELETE": re.compile(r"(?i)\bDELETE\s+FROM\s+([`\"\[]?[\w]+[`\"\]]?)", re.MULTILINE),
}

METHOD_PATTERN = re.compile(
    r"(?:public|private|protected|static|\s)+[\w<>\[\]]+\s+([\w]+)\s*\([^)]*\)\s*\{",
    re.MULTILINE
)

def extract_java_method_sql(filepath: str) -> dict:
    with open(filepath, encoding="utf-8", errors="ignore") as f:
        content = f.read()

    # メソッドごとに分割(簡易)
    method_map = {}
    methods = list(METHOD_PATTERN.finditer(content))

    for i, m in enumerate(methods):
        method_name = m.group(1)
        start = m.end()
        end = methods[i + 1].start() if i + 1 < len(methods) else len(content)
        body = content[start:end]

        tables = {}
        for op, pat in SQL_PATTERNS.items():
            found = [t.group(1).lower() for t in pat.finditer(body)]
            if found:
                tables[op] = list(set(found))

        if tables:
            method_map[method_name] = tables

    return method_map

# ── 使用例 ────────────────────────────────────────────────
if __name__ == "__main__":
    import json
    result = {}
    for java_file in Path("./java-src").rglob("*.java"):
        m = extract_java_method_sql(str(java_file))
        if m:
            result[str(java_file)] = m

    print(json.dumps(result, ensure_ascii=False, indent=2))

活用シーン

レガシーシステムのリプレイス案件では、旧システムのソースコードから「どの機能がどのテーブルを操作しているか」を把握するだけでも詳細設計の工数が大幅に削減できる。SQLパターンマッチは100%ではないが、見逃しのスクリーニングとして非常に有効だ。

レビューチェックリスト

機能詳細設計書のレビュー前に以下を確認すること。

#チェック項目
1機能IDが基本設計の機能一覧と一致しているか
2主フロー・代替フロー・例外フローの全経路が網羅されているか
3条件分岐の条件式が具体的な値・コード・式で記述されているか
4計算式・丸め方式・税率が明示されているか
5DB操作対象テーブルが全件列挙されているか(参照・更新・挿入・削除)
6例外フローにHTTPステータス・メッセージID・ログレベルが記載されているか
7排他制御の方式(楽観/悲観/べき等)が定義されているか
8外部サービス呼び出しのタイムアウト・リトライ方針が定義されているか
9非機能要件(応答時間・上限件数)への言及があるか
10テスト担当者が読んでテストケースを設計できる粒度か