💨

[PHPStan]nullチェックを綺麗に可憐に捌きたい💃

に公開

いつもお疲れ様です。
今日はnullチェックのお話をします。

背景

弊チームでは、つい最近PHPStanのレベルを6から8に上げました。
レベルアップにともない特に重要となるのが、null許容のオブジェクトに対するnullチェックです。
その中でも、DBモデルのリレーションのnullチェックが話題になりました。
弊システムには外部キー制約がついていないDBテーブルが存在するため、本来nullにはならない(直接クエリで削除しない限りnullにならない)リレーションもnull許容の型とみなされてしまいます😔
そのため多くのDBモデルで、リレーション取得時にnullチェックが必要になりました。

弊システムはnullチェック漏れでバグを発生させてしまったことが過去に何度かあるので、nullチェックにコストをかけることにはみんな賛成でした😊(多分)
ただ、いろんなところにnullチェックが散乱してしまってコードが読みにくくなるのではと少し懸念しております🤔

そこでnullの性質別に、nullチェックをする場所や書き方などを整理していきたいと思います。

nullの性質別対処方法

nullといっても、さまざまな性質があります。
こちらの記事でnullの分類について触れています。
https://zenn.dev/kkyoka/articles/7341f151f0b7bc

今回は異常ケースと正常ケースのふたつに分けて考えます。

そもそも仕様上null不可であるべきものはDBで制御しておきたいところですが、DBの仕様変更はコストもかかるし難易度も高いので、現在のDBの仕様を受け入れた上でアプリケーションでどう対応すべきかを考えます🫡

異常ケースのnull

システムの仕様上はnullになりえないが、DBでnullableになっていたり外部キー制約が付いていなかったりする場合。
この場合はDB側都合なので、リポジトリ層でnullチェックしておきたい。
異常値なので、後続処理に影響が出るためnullを検知した場合は例外を投げて処理をストップさせる。

正常ケースのnull

システムの仕様上、nullを許容している場合。
特定のステータスにならないと格納されない値や、オプショナルな値など。
この場合は業務ルール都合なので、ドメイン層でnullチェックをしておきたい。
nullを検知した場合、ドメインモデルや値オブジェクトなどに変換することで、nullを見えない状態にする。

まとめ

上記をまとめると以下になります。
ふたつのパターンで対応方法を分けることで、nullが意図的なものかそうでないかをコード上で理解することができます。

nullの性質 処理すべき層 対処方法
異常ケースのnull リポジトリ層 例外を投げて処理をストップ
正常ケースのnull ドメイン層 ドメインモデルや値オブジェクトなどに変換する

「PHPStanでエラーが出たから対処する」だと、ただエラーが出た箇所でnullチェックして終わりになってしまいます。
そうではなく、なぜnullになってしまうのか、どこでnullチェックすべきか(nullチェックはどの層の責務か)を考えて対応できれば良いな〜と思いました🐈

nullチェックの必要性が高くなったことで、ドメインモデルや値オブジェクトの設計がより重要になると思っています。
ええ感じにクラス設計できたらいいね。

感想

nullableカラム多すぎわろた
これから苦労しそうでそうろう...

Discussion