Slide 1

Slide 1 text

BuriKaigi 2025 例外処理を理解して、 設計段階からエラーを 見つけやすく、起こりにくく 梶川 琢馬 @kajitack

Slide 2

Slide 2 text

梶川 琢馬 (Takuma Kajikawa) 2023年に中途入社、リアーキテクトやデザインシステムに取り組んでいます。 趣味はトライスロンやランニング。
 TechBowl社内でもランニングを報告するチャンネルがあります。 富山県高岡市出身ですが、BuriKaigiは初参加です! 株式会社 TechBowl プロダクトエンジニア @kajitack

Slide 3

Slide 3 text

https://techtrain.dev/engineer-type-diagnosis #エンジニアタイプ診断 自己紹介に使ってみてください!

Slide 4

Slide 4 text

9 例外処理を設計段階に組み込んだ背 9 例外処理を理解す 9 例外を分類す 69 設計に組み込Ç Æ9 例外を使わないパターンを考えてみる 今日話すこと webアプリケーションの想定。コード例はPHPですが、例外処理全般について話します。

Slide 5

Slide 5 text

U 例外処理の役割を理解できるようにす4 U エラーが「見つけやすい」仕組みや「そもそも起きにくい」コードの設計をチーム でディスカッションするためのヒントを得る 今日のゴール

Slide 6

Slide 6 text

例外処理を設計に組み込んだ背景

Slide 7

Slide 7 text

→プロダクトの複雑化 例外処理を設計段階に組み込んだ背景

Slide 8

Slide 8 text

TechTrainについて 本気で、 エンジニアキャリアを伸ばす、 エンジニア塾。 “本気で実力をつけたい。”

Slide 9

Slide 9 text

背景: プロダクトの複雑化 もともとはエンジニアメンターに メンタリング、技術相談ができるサービスとして開始。

Slide 10

Slide 10 text

背景: プロダクトの複雑化 個人向けのサービスから、法人向けにも拡大 扱うドメイン知識が増えた

Slide 11

Slide 11 text

複数のサービス、無数のクラスの ルールがある メンターとの面談機U 5 面談予約を作るときは引数は〇〇とXXが必6 5 面談予約をキャンセルする場合は開始前である必要 こういったルールを違反した場合に例外処理を行いたい。 ドメインモデル図 オブジェクトに対する制約 背景: プロダクトの複雑化

Slide 12

Slide 12 text

例外処理を理解する

Slide 13

Slide 13 text

1. エラーの発生を明示的に知らせて処理を中断する 2. 通常の制御フローとエラーハンドリングを分離する 3. エラー情報を詳細に保持し、デバッグを容易にする 例外の役割

Slide 14

Slide 14 text

エラーの発生を明示的に知らせて処理を中断する

Slide 15

Slide 15 text

https://speakerdeck.com/twada/growing-reliable-code-phperkaigi-2022 予防に勝る防御なし - 堅牢なコードを導く様々な設計のヒント

Slide 16

Slide 16 text

throwすることで処理が中断される 例外を投げることによって処理を中断できる DBへの保存前にデータの整合性をチェックしている

Slide 17

Slide 17 text

通常の制御フローとエラーハンドリングを分離する

Slide 18

Slide 18 text

0 エラーチェックが増えてコードが読みにく$ 0 エラーメッセージの処理が分散していて、保守がしにく$ 0 異常が起きても単にメッセージを表示するだけ if 文でエラー処理を行う場合

Slide 19

Slide 19 text

G 異常が発生した場合は throw で即座に処理を中断し、呼び出し元に通 G エラーを区別できÉ G 呼び出し側に中断後の処理を任せることができる 例外を使って異常系を分離する

Slide 20

Slide 20 text

エラー情報を詳細に保持し、デバッグを容易にする

Slide 21

Slide 21 text

https://speakerdeck.com/twada/growing-reliable-code-phperkaigi-2022 予防に勝る防御なし - 堅牢なコードを導く様々な設計のヒント

Slide 22

Slide 22 text

そのままクラッシュするわけにはいかない... 例外オブジェクトが持つ情報をもとにエラーの内容を伝える APIのレスポンスに変換 ログへの書き込みやアラート

Slide 23

Slide 23 text

1. 通常の制御フローとエラーハンドリングを分離する if 文でエラーをチェックする代わりに、例外を用いることで正常系と異常系の処理を明確に分けることができる。 例: try-catch を使うことで、異常が発生した際の処理を一箇所にまとめられる。 2. エラーの発生を明示的に知らせる 例外をthrowする可能性があることをコードを通して、知らせる事ができる。 例えば、不正な入力 (InvalidArgumentException) や、リソースの取得失敗 (IOException) など。 3. エラー情報を詳細に保持し、デバッグを容易にする スタックトレースを提供し、エラー発生場所や原因を特定しやすくする。 例: RuntimeException("User not found") は、単なる return null よりも原因が明確。 例外の役割

Slide 24

Slide 24 text

例外を分類する

Slide 25

Slide 25 text

) 実装や技術的な問題で起きるエラ( 6) ユースケースで想定できる例外 エラーと例外というふうに分けてますが、例外処理では同じ扱い 例外の種類

Slide 26

Slide 26 text

実装や技術的な問題で起きるエラー ユーザーが通常のユースケースとして回避できないエラー。HTTPステータスでいう500系 起きる原) & バD & DBや外部APIなどの接続エラ & データの不整合 ユースケースで想定できる例外 クライアント側の問題。HTTPステータスでいう400系 起きる原) & アプリケーション特有の制約違€ & (例) 予約する際の件数制限を超えていた

Slide 27

Slide 27 text

誰の責務か?をハッキリさせる e ユーザーからの入力がおかしいと9 e クラスの処理の事前条件を満たせないと9 e ドメインロジックによって起きたと9 e DBや外部APIからデータ取得したとき

Slide 28

Slide 28 text

Presentation Infrastructure Application Domain Presentation Infra Application Domain 処理の流れ 例外の流れ 責務ごとにレイヤーを分けて考えていきます 依存の方向

Slide 29

Slide 29 text

Presentation Httpのステータスコードなど、プロトコルやUIに密接な情報を持つ例外 ユースケースの問題を表す例外(Presentationから見ると400系) DBの接続エラーやライブラリが投げる例外 「口座の残高不足」など、ドメインロジックの中で想定する例外 Infra Application Domain 各レイヤーで投げる例外 例外の流れ

Slide 30

Slide 30 text

設計に組み込む

Slide 31

Slide 31 text

No content

Slide 32

Slide 32 text

62 入力値としてメールアドレスを受け取 2 仮登録状態にす 42 仮登録メール経由で、登録フローに遷移 新規登録の流れ

Slide 33

Slide 33 text

Presentation Infrastructure Application Domain Presentation Infra Application Domain 処理の流れ 例外の流れ 責務ごとにレイヤーを分けて考えていきます 依存の方向

Slide 34

Slide 34 text

F@ 入力値としてメールアドレスを受け取 "@ 仮登録メールアドレスValueObjectを作È B@ 仮登録Entityを作È #@ DBに保存 Presentation Infra Application Domain ドメインモデルを中心とした処理の流れ

Slide 35

Slide 35 text

入力値としてメールアドレスを受け取る Presentation Application 入力値のバリデーションを行I H メールアドレスの形式がおかしb H 想定できる例外

Slide 36

Slide 36 text

ハンドリング(入力値のバリデーションエラー) 想定できる例外が投げられたのでハンドリング 投げられた例外は誰の責務? →フロントエンドのバリデーションミスによるもの Presentationレイヤー(FW)で 例外をHTTP レスポンスに変換す„ P ステータスコード 422 で返% P 詳細なエラーメッセージを返す Presentation

Slide 37

Slide 37 text

メールアドレスValueObjectを作成 Application コンストラクタで形式をチェッb P 呼び出し側の実装ミス(Application層のバリデーション) →バグ Domain

Slide 38

Slide 38 text

ハンドリング(呼び出し側の実装ミス) 例外をHTTP レスポンスに変換す7 5 ステータスコード 500 で返8 5 「予期せぬエラーが発生しましたQ 5 エラーレポートする Presentation

Slide 39

Slide 39 text

仮登録Entityを作成 Domain Entity作成前にメールアドレスが仮登録・本登録済みでないかを チェッX 4 もし登録済みだった場 4 想定できる例外

Slide 40

Slide 40 text

ハンドリング(登録済みだった) 投げられた例外は誰の責務? →ユースケース違反 利用方法が違うことを表す例外を投げる 例外をHTTP レスポンスに変換すg f ステータスコード 422 で返i f 詳細なエラーメッセージを返す Presentation Application

Slide 41

Slide 41 text

DBに保存 Domain Infra DAOを通じてValueObjectの値を取り出して永続I ) DB接続エラR ) テーブル定義とのデータ型ミスマッチ →バグ

Slide 42

Slide 42 text

ハンドリング(DBへの永続化エラー) 例外をHTTP レスポンスに変換す7 5 ステータスコード 500 で返8 5 「予期せぬエラーが発生しましたQ 5 エラーレポートする Presentation

Slide 43

Slide 43 text

一箇所にまとめるHandlerを作る (FWの機能を利用する) APIのレスポンスに変換 ログへの書き込みやアラート Presentation ハンドリング全般

Slide 44

Slide 44 text

想定外の例外が投げられないことをデバッグする r 検証環境の段階でエラーレポートやログを確認でき8 r IDEで例外クラスを指定して、BreakPointが貼ってデバッグできる

Slide 45

Slide 45 text

想定した例外を投げることをテストする x 戻り値のチェック等と同様に例外が投げられるかをテストす0 x 投げるべきところで投げてないなら他の箇所でエラーになる可能性が高い →テストしておくことで、他の箇所ではエラーが起きにくくなる

Slide 46

Slide 46 text

想定できる例外はコンテキストごとに違う チームで議論しながら例外をモデリングする ドメインモデル図 オブジェクトに対する制約

Slide 47

Slide 47 text

ドメインモデル図 オブジェクトに対する制約 https://zenn.dev/techtrain_blog/articles/334ac36e79d946 モデル図の詳細はこちら

Slide 48

Slide 48 text

3 責務によって例外を分け@ 3 想定外のエラーを「見つけやすい2 3 テストコードで想定する例外をテストすることで他の箇所では「起きにくい」 例外を設計に組み込むことによって...

Slide 49

Slide 49 text

例外を使わないパターンを考えてみる

Slide 50

Slide 50 text

例外処理の課題 m goto文のように流れが変わるので、どこでエラーが起こるか分かりづらƒ m try-catch で囲まれていないと、例外が上位の関数まで伝播してしまい、意図しない挙動を引 き起こすことがあ“ m 例外は関数に明示されないため、どの関数が例外をスローするかが不明確になりがv m throws宣言のようなチェックする機構もあるが、開発者によってまちまv m 何を例外とするかが曖昧になりがち

Slide 51

Slide 51 text

想定できる例外って...?

Slide 52

Slide 52 text

例外とは、何か例外的なもの(ある意味、あり得ない)を表現するもの であるとした場合、 失敗は想定できる結果であるため、その失敗を例外としてモデリングす ることは概念的におかしい。 そのため、処理失敗は成功したときと同じように扱う形で設計をしてモ デリングします。 例外というよりは失敗

Slide 53

Slide 53 text

Result型 成功と失敗のどちらかを明示的に表現できる • 成功 (Ok(T)) → 正常な値 T を返す • 失敗 (Err(E)) → エラー情報 E を返す 戻り値として型が明示されるため、エラーが発生する可能性が一目で分かる 定義したResult型を利用 例外ではなく戻り値で結果を返す

Slide 54

Slide 54 text

例外との混在はどうする? Presentation Infrastructure Application Domain 依存の方向 ドメイン層でのみResult型を使用し、 Application層で例外を投げる StackTraceが課題...?

Slide 55

Slide 55 text

現状は、Result型は取り入れてない y 例外クラスの設計をしっかりやv y 例外を関数のdocで宣言し、静的解析でチェックすv y 言語に組み込まれているなら積極的に使っても良さそH y 設計が重要なのは例外処理と同様

Slide 56

Slide 56 text

w チームの中で例外処理についての認識を合わせE w 設計でエラーを「見つけやすく」「おきにくく」しE w 例外機構を持たない言語では戻り値でエラーを表現す w 例外機構を持つ言語でもResult型を取り入れる検討の余地がありそう まとめ 皆さんのプロジェクトではどうしてるか?ぜひ聞かせてください!

Slide 57

Slide 57 text

ご清聴ありがとうございました! TechTrainではメンターを募集中です! 興味ある方はぜひ声かけてください! ↑メンターの活動一部抜粋