はじめに
この記事ではプロを目指す人のための例外処理(再)入門 / #rubykansai 2018-01-13で学習した内容を備忘録としてTypescript版にしつつまとめたものになります。
前編はこちらです
プロを目指す人のための例外処理(再)入門 をTypeScript版でまとめてみた 前編
後編では例外処理(主に設計)に関する高度なトピックとして紹介されている箇所をとりあげますので是非ご覧ください!
業務エラーとシステムエラーの違い
エラーには概ね以下の二種流があります
- 業務エラー(ビジネス例外)
- ユーザー操作が原因となるエラー
- 入力フォームにおけるユーザーの入力ミスや権限違反(権限のないページへのアクセス等)等
- システムエラー(技術的例外)
- システム側の都合によるエラー
- プログラムのミス(いわゆるバグ)、ネットワークエラー、データベースのダウン等
これらの区別をすることで、適切なエラーへの対処をすることができます
業務エラーへの対処
業務エラーが起きた場合は、以下のような対応が求められます
ユーザー側にやりなおしをさせる
ユーザー側に原因がある場合、ユーザーの挙動に問題があります。例えば数値を入力する部分に文字列を入力した場合など、、、ですのでエラーをユーザーに伝えてユーザーに操作をやり直してもらうしかないのです
例外処理を極力使わずに対応する
業務エラーは基本的にシステム側で想定できたり、検知できるものばかりです。ですので例外処理を使うケースはあまりありません。
export const createEvent = async (req: Request, res: Response) => {
const event = new Event(req.body); // リクエストから新しいイベントを作成
// 保存メソッドの戻り値をチェック
const savedEvent = await event.save().catch(() => null);
if (savedEvent) {
// 保存成功時
res.redirect(`/events/${savedEvent.id}`, {
notice: 'Event was successfully created.',
});
} else {
// 保存失敗時
res.render('new', { event, error: 'Event creation failed.' });
}
};
このコードのように成功or失敗を例外を使わずともメソッドの戻り値で表現できます。この戻り値を検証してエラーハンドリングすれば十分エラーに対処できます。逆に例外処理をなくしても業務エラーに対処でない場合、処理フローの制御に例外処理を使っています。その場合には例外処理を使わない業務フローを検討しましょう。(もちろん使わなくてはどうにもならな場合もありますが!)
システムエラーへの対処
主に以下の対応になります
- 画面には500エラーを出し、ユーザーにこちらで対処する旨を伝える
- エラーログを出す
- 処理を中止するorやむを得ない場合のみcatchして続行する
基本的には処理をそのまま停止させてOKです。ただしやむを得ない場合はcatch等をして続行しましょう。ここでいうやむを得ない場合とは、前編で紹介した例外処理を使っても許されるケースで紹介した場合やそのまま停止した場合にデータの整合性が合わなくなってしまうときです
例外処理とロールバックの関係
アトミック操作
アトミック操作(不可分操作)とは以下の性質を持つ操作です
- 全ての変更が成功したときのみ、変更を確定する
- 1つでも失敗すればロールバックして、変更前の状態に戻す
スライドでは例として複数のレコードを同時に更新する場合やデータベース更新後に課金決済APIを呼び出す場合が紹介されています。
アトミック操作はデータの整合性を担保するのに必要不可欠です。トランザクションを用いたり、手動によるロールバックを用いて、エラーが起きた時にもデータの整合性を担保できるようにしましょう。
攻めの例外(意図的に例外を発生させる)
予期せぬ条件分岐を防ぐ
以下のコードのように、分岐した先でありえない分岐をした場合には例外を投げてエラーを発生させて処理を停止しましょう。これを放置していると予期せぬ動作につながります。例えば変なところでエラーが出たり、矛盾した動作を起こしたりするので気をつけましょう。
function currencyOf(country: string): string {
switch (country) {
case 'japan':
return 'yen';
case 'us':
return 'dollar';
case 'india':
return 'rupee';
default:
throw new Error(`無効な国名です。${country}`);
}
}
// 使用例
try {
console.log(currencyOf('italy')); //=> Error: 無効な国名です。italy
} catch (error) {
console.error(error.message); // エラー処理
}
まとめ
以上、プロを目指す人のための例外処理(再)入門 をTypeScript版でまとめてみた 後編でした!
自分もまだ例外処理については学習中ですが、同じ学習を進めている方の参考になればと思います!