メインコンテンツへスキップ

全体処理フロー(ユースケース手順)

0) 起動〜前処理

  • CLI 引数を解釈(-i <yaml> 必須)
  • ロギング初期化(ログは stderr / stdout とは分離)
  • 入力ファイル存在チェック(無ければ入力エラーで即時終了)

1) YAML 読み込み(入力→ドメイン)

  • YAML を読み込み、ドメインモデル(Book/Chapter/Page)へ変換
  • ここでトリム等の正規化(前後 trim)は 変換時に実施(以後は正規化済みを前提にする)

2) ドメイン検証(API を叩く前に止める)

  • 必須チェック・型チェック・重複チェックなど(要件にある “順序が意味を持つ” を前提に、順序自体は保持)
  • 検証 NG は 入力エラー として即時終了(API は一切呼ばない)

3) 事前取得(同一性判定に必要な最小情報だけ)

  • 対象 Book の既存構造(Book直下 pages / chapters / chapter配下 pages)を API で取得
  • 取得するのは同一性判定に必要な最小フィールド(例: id, name, parent 等)に限定

4) Book 直下ページ処理(YAMLの順序通り)

  • YAML の pages を上から順に処理

  • 各ページについて:

    • 同一性判定(既存があるか)
    • あれば SKIP
    • 無ければ CREATE → OK

5) Chapter 処理(YAMLの順序通り)

  • YAML の chapters を上から順に処理

  • 各 Chapter について:

    • 同一性判定(既存があるか)
    • 無ければ CREATE → OK
    • あれば SKIP
    • 以後、その Chapter コンテナ(id)を確定させる

6) Chapter 配下ページ処理(章ごと、YAMLの順序通り)

  • 各 Chapter の pages を上から順に処理

  • 各ページについて:

    • 同一性判定
    • あれば SKIP
    • 無ければ CREATE → OK

7) 終了

  • 全処理が成功/スキップで完了したら終了コード 0

3.2 エラー発生時の即時終了ルール

即時終了の原則を先に固定しておくと、実装がブレない。

A. 入力エラー(YAML/検証)は「即時終了・部分実行なし」

  • YAML パース不能
  • 必須項目不足
  • 型不正
  • 重複や同一性ルール上の矛盾(設計で決めた NG 条件)
  • 終了コード:入力エラー(例: 2)、stdout には最後に ERROR を出す(下記参照)

B. API 実行エラーは「即時終了」

  • 途中まで作ってしまっても “破壊操作なし・再実行で復旧” が前提なので、基本は即時終了でよい
  • 終了コード:実行エラー(例: 1)

C. 例外(想定外)は「即時終了」

  • → 実行エラー(1)扱いでよい(内部ログにスタックトレース)

3.3 API リトライ適用範囲(どこでやるかも含めて固定)

リトライの配置

  • BookStackClient(インフラ層)に集約 ユースケース層は「API を呼ぶ/呼ばない」の判断だけを持つ。

リトライ対象(推奨)

  • ネットワーク系(タイムアウト、接続失敗、DNS 等)

  • 5xx(サーバエラー)

  • 429(レート制限)※ Retry-After があれば尊重

  • GET/POST とも対象にできるが、POST は冪等性が崩れやすいので 注意事項を仕様に書く

    • “作成 API が二重作成を起こし得る” 場合:POST は原則リトライしない か、あるいは「作成直後に同一性再チェックして二重作成を抑止」などの方針を明文化(どちらかに寄せて決め打つ)

リトライ非対象(推奨)

  • 4xx(400/401/403/404/422 など)※入力や権限や存在の問題なので再試行しても改善しない
  • JSON decode 失敗などのアプリ側バグ疑い

3.4 標準出力イベント(OK / SKIP / ERROR)の定義

stdout は「機械/人が追える最小ログ」に限定して、ログ本体は logger 側へ。

1行1イベント(推奨フォーマット例)

  • OK <kind> <path> <id?> <name>
  • SKIP <kind> <path> <id?> <name> (reason=<...>)
  • ERROR <phase> <message>

ここで:

  • <kind>: BOOK_PAGE | CHAPTER | CHAPTER_PAGE
  • <path>: 例 book/pages[03] chapters[01] chapters[01]/pages[02]
  • <id?>: 作成した/既存の id を出せるなら出す(後追いデバッグが楽)

ERROR 出力のタイミング

  • 入力エラー:検証で落ちた時点で ERROR 1行出して終了
  • API エラー:リトライ尽きた時点で ERROR 1行出して終了

3.5 「どのモジュールにどの機能を持たせるか」案(責務分割)

実装前提の分割案。名前は例で、責務境界が重要。責務境界依存方向を固定するのが目的。 (client は外界、domain は純粋、usecase が意思決定、という軸を崩さない)

📁 想定ファイル構成(案)

.venv/
.vscode/
bookstack_skeleton/
  __init__.py
  main.py
  loader.py
  domain.py
  validator.py
  usecase/
    __init__.py
    apply.py
  identity.py
  client.py
  reporter.py
docs/
tests/
  test_main.py
  test_loader.py
  test_domain.py
  test_validator.py
  test_usecase_apply.py
  test_identity.py
  test_client.py
  test_reporter.py
create_skeleton.py
  • usecase.apply.py は Python 的に不自然になりやすいため、usecase/apply.py に分割する
  • create_skeleton.py は CLI エントリ(薄いラッパ)で、実処理は bookstack_skeleton.main に寄せる

bookstack_skeleton.main(エントリポイント)

  • CLI 引数の解釈
  • ログ初期化ロギング初期化(stdout と分離)
  • ユースケース呼び出し入力ロード → 検証 → ユースケース呼び出しの全体制御
  • 例外整形・終了コード返却

bookstack_skeleton.loader

  • YAML の読み込み
  • ドメインへ変換(正規化:trimYAML 等)→ ドメインへの変換
  • 正規化(例:前後 trim)は ここで完了し、以後は「正規化済み」を前提とする
  • 「YAML構造→ドメイン」の責務のみ(検証は別)検証は validator に分離)

bookstack_skeleton.domain(dataclass 群)

  • BookSpec / ChapterSpec / PageSpec 等(命名は任意)

  • API や YAML を意識しない純粋なデータ構造

  • ドメイン不変条件(軽いバリデーションはここでも可。ただし“入力エラー一覧”を出したいなら軽いバリデーションはここでも可)

    • ただし「入力エラーを複数件まとめて提示」したい場合は validator に寄せる方が実務的)に寄せる
  • 前提:loader が正規化済みの値を渡す(domain 側で trim はしない)


bookstack_skeleton.validator

  • ドメイン検証(必須・型・重複・規約)
  • エラーは「複数件まとめて」返せる形が望ましい(ユーザーの修正コストが下がる)ユーザー修正コスト削減)
  • 前提:ここで落ちた場合は 入力エラーとして API を叩かずに終了(main の責務)

bookstack_skeleton.usecase.apply(ユースケース本体)

  • 処理順の固定(Book直下→Chapter→Book直下 → Chapter → Chapter配下)
  • 同一性判定の呼び出し事前取得(同一性判定に必要な最小情報)と、その結果の保持
  • 同一性判定(identity)の呼び出し
  • “作る/作らない”作らない(SKIP/CREATE)” の意思決定
  • stdout イベントの発行(Reporterreporter 経由でも良い)経由)

bookstack_skeleton.identity

  • 同一性判定ルール(Book直下ページ / Chapter / Chapter配下ページ)
  • “SKIP 条件” をここで一元化(= を一元化(判定結果に reason を持たせる)
  • client は呼ばない(外界依存を持たない)。必要な既存情報は usecase が渡す

bookstack_skeleton.client(BookStackClient)

  • BookStack API 呼び出しの薄いラッパ
  • リトライ(範囲・回数・待機)をここに集約
  • レート制限対応(必要なら Retry-After 等)もここ
  • 取得最適化(最小フィールドだけ取る」など取得最適化もここ同一性判定に必要な最小フィールドだけ取る」)もここ
  • 業務ルール(同一性、SKIP、処理順)は一切持たない

bookstack_skeleton.reporter任意)stdout 出力)

  • stdout のイベント整形(OK/SKIP/OK / SKIP / ERROR の1行化)
  • main/表示フォーマットをここに固定し、main / usecase から分離したい場合のみを汚さない
  • 将来の拡張(quiet、JSON、CI向け)もここで吸収できるようにする