ビジネスロジックのバリデーションは DomainException を継承したものを投げる
バリューオブジェクトやエンティティやドメインサービスで投げられることの多いビジネスロジックのバリデーションの例外ですが、
その例外はphpstormのdocコメントにて@throws
が必要な(Exceptionをextendしたもの)で投げるべきかコメントが必要ないDomainException
を継承したもので投げるべきかを悩んでいたのですが、DomainException
を継承したもので投げるべきだと私の中で答えがでました。
入力フォームなどでビジネスロジックのバリデーションの例外をキャッチして、Viewに表示したいことがあり、@throws
が必要な例外だとcatchすることが明示的なのでそっちかなと思ったのですが。
下記のスライドを見てビジネスロジックの例外を投げるクラスからしたら、システムを落とすように例外を投げることを推奨していたので DomainException
を継承してビジネスロジックのバリデーションは投げるべきだなと決めました。
では、落とすように例外をなげたらそれをキャッチしてはいけないかというとそうではなくて、アーキテクチャ/フレームワーク等では逆に落とさないようにcatchすることを推奨しています。
https://speakerdeck.com/twada/php-conference-2016?slide=56
こちらのスライドである通り、
1 | ミクロでは正当性を重視し、マクロでは堅牢性を重視する |
とありました。
正当性と堅牢性とか意味が分からなければ、このページの5ページ前ぐらいから読むといいと思います。
また、Viewに表示したいから@throws
が必要な例外を投げるとかドメインがViewに配慮する考え方もおかしいですよね。
ビジネスロジックのエラーは例外的状況である
そもそもビジネスロジックのバリデーションで例外を投げるのはおかしい、
例外は例外的な状況にだけ利用するべきだと意見されたことがあるのですが、
それは、例外的な状況
の縮小解釈です。
https://qiita.com/kata/items/bd129ba6113a61126389#%E9%A0%85%E7%9B%AE57-%E4%BE%8B%E5%A4%96%E7%9A%84%E7%8A%B6%E6%85%8B%E3%81%AB%E3%81%A0%E3%81%91%E4%BE%8B%E5%A4%96%E3%82%92%E4%BD%BF%E7%94%A8%E3%81%99%E3%82%8B
に書かれている通り、
1 | ArrayIndexOutOfBoundsExceptionを使用して配列のループ処理の脱出するなどアホな事はしない |
このレベルでの状況で例外を使うなということで、ビジネスロジックのバリデーションエラーについては例外的状況と捉えてよいと考えます。
また、こちらのスライドの14ページに
https://www.slideshare.net/t_wada/exception-design-by-contract
1 | 例外は例外的な状況にだけ利用するべき |
とありますが、この通常の制御フロー
を示すものも前のページでのループを抜ける制御のレベルで使うなといっていますし。
15ページの
1 | 例外は例外的な問題のみに使用すること |
についても、ビジネスロジックの例外のキャッチをすべて行わなくても動作することはできます。
29ページにて
1 | 技術的例外とビジネス例外を明確に区別する |
ビジネスロジックのバリデーションの例外を準正常系として利用することを認めています。