boy in green shirt
Photo by CDC on Pexels.com

【授業レポート】システム開発(2年) 第38週目

〜エラーハンドリング徹底強化:失敗を制御する設計〜

第38週目は、先週の結合テストで露呈した多くの“バグ”と“例外”を題材に、
エラーハンドリング(例外処理・入力バリデーション・UI側のメッセージ設計) を体系的に学ぶ回となりました。

アプリが動くようになった今こそ、「どう壊れるかを設計する」ことが必要だと先生は強調しました。


■ 先生の導入:「エラーを放置するアプリは信頼されない」

田中先生:「上手く動くときだけ正しくてはダメ。
“失敗したときにどう振る舞うか”まで設計できて初めて、プロのシステムになります。」

先生は、先週の結合で出た代表的な失敗例を紹介:

  • DB更新失敗 → 中途半端な状態になり在庫情報がおかしくなる
  • 入力値エラーが UI に丸見え(例:ValueError の生テキスト)
  • 例外がキャッチされずアプリごと停止
  • 異常時のログが不足して原因追えず

これらを「今週の教材」として扱い、改善方法を学びました。


■ 今日の3本柱(テーマ)

  1. 入力バリデーション(UI層)
  2. 例外設計(Service層)
  3. 失敗の記録と通知(ログ&DAO層)

■ 実習①:入力バリデーションを UI で行う

アプリの多くのバグは “想定外の入力” が原因。
そこでまず UI層での防御 を徹底しました。

例:数値 ID が必要な場面

raw = input("Book ID: ")

if not raw.isdigit():
    print("入力エラー:IDは数字で入力してください。")
    return
book_id = int(raw)

生徒A:「数字チェックを入れただけでエラーが半分減った!」

バリデーションの原則(先生より)

  • UIでできるだけ “機械的に弾く”
  • ただし “業務的チェック” は Service でやる
  • エラーメッセージは “ユーザーに理解できる言葉” で書く

■ 実習②:サービス層の例外設計(ドメイン例外とシステム例外の区別)

サービス層は業務ロジックを担当するため、
エラーも分類して扱うとコードが読みやすくなります。

例:ドメイン例外のクラスを作る

class BookNotFoundError(Exception):
    pass

class BookNotAvailableError(Exception):
    pass

サービス層での使い方

book = self.books_dao.get(book_id)
if not book:
    raise BookNotFoundError()

if not book.is_available:
    raise BookNotAvailableError()

UI層では、例外に応じて適切なメッセージを表示するよう整理。

生徒B:「例外に名前を付けるとエラーの意図が一気に分かりやすくなった!」


■ 実習③:DAO層での失敗制御とトランザクション管理

DAO層では次のような対策を実装:

  • DB操作失敗時は 必ずロールバック
  • DAO内部のSQLエラーはログへ(UIに出さない)
  • トランザクション境界は Service で管理する

例:DAO内部の例外処理(学習用)

try:
    cur.execute("UPDATE books SET is_available=%s WHERE id=%s", (flag, book_id))
    self.conn.commit()
except Exception as e:
    self.conn.rollback()
    logger.error(f"DB Error: {e}")
    raise

生徒C:「DAOでのロールバック忘れが原因の“在庫ズレ”バグが解消できた!」


■ 実習④:UI向けエラーメッセージのデザイン

UI側に “そのまま例外” を出すのはNG。
ユーザー向けの分かりやすい文言に変換する演習を行いました。

before(悪い例)

ValueError: invalid literal for int() with base 10

after(改善)

入力エラー:本IDは数字で入力してください。

学習ポイント

  • “何が悪いか” を具体的に
  • ユーザーに責任転嫁する表現は避ける
  • 再入力方法を示すと親切

■ 実習⑤:ログ(アプリ内部向け)と UI(ユーザー向け)の役割分担を明確にする

先生はホワイトボードに大きく次の式を書きました:

**ログは開発者のため

エラー表示はユーザーのため**

その上で、次の2段階処理を推奨:

  1. Service or DAO で詳細をログへ書く(内部用)
  2. UI では簡潔で優しいメッセージを表示(外部用)

生徒D:「ログに“どのIDで失敗したか”が残ってると、調査がすごく楽!」


■ プチ演習:エラーパターンを10種類書き出す

班ごとに以下のカテゴリでエラーを列挙し、どの層で処理すべきか整理しました:

  • 入力エラー
  • 権限エラー
  • 業務ロジックエラー
  • DAOの整合性エラー
  • 外部APIの失敗
  • トランザクション失敗
  • 予期しない例外(フォールバックを考える)

生徒E:「分類すると“どこで捕まえるべきか”が答えやすくなる!」


■ 先生のまとめのひとこと

「エラー処理は“安全装置”です。
ユーザーとデータを守る最終防衛ラインとも言えます。

正しく動くコードより、
“間違っても壊れないコード”の方が価値が高い。

今日学んだ設計は、今後の大きな開発やチーム開発で必ず役に立ちます。」


■ 宿題(来週の実装仕上げに向けて)

  1. 班で “主要ユースケースのエラー一覧表” を作成
  2. UI向けの 改善済みエラーメッセージ10種類 をまとめる
  3. 今日修正したエラーハンドリング部分の ミニ結合テストレポート を提出

■ 来週の予告:ユースケース完成&ミニ発表会

次週は、強化したエラーハンドリングを含め、
ユースケース単位での“完成形デモ” を行います。
アプリがどれだけ安定して動くかを披露する回です。


エラーと向き合い、
“壊れないアプリ”へ大きく前進した第38週目。
生徒たちは実装者としての視点だけでなく、
“ユーザーの安心” を設計する力も身につけ始めています。

投稿者 greeden

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)