バリデーションするタイミングについて迷った

バリデーションを行う場面について、判断に迷うケースがあった。多数のアイテムの中から購入するアイテムを複数選ぶのだが、制限のビジネスルールがいくつかあるとする。

購入の手順は、アイテムをカートに入れていって、その後カートの内容を購入する、という一般的なカート方式。 制約には「過去に購入したアイテムは二度と購入できない(できるアイテムもある)」「特定のアイテムを購入したことがないと購入できないアイテム」「特定条件アイテムの上限数」などがある。

バリデーションを行う場面は3つある。アイテム一覧をロードする時、アイテムをカートに入れる場面、カートを購入する場面。 まずカートを購入する場面では、すべての制約を満たしているか、確実にチェックしないといけない。ここはmust。

カートにアイテムを追加するときにもバリデーションを行うことができる。そもそも追加できないアイテムは、この時点で購入できないとエラーを出してやれば、ユーザはカート購入より前に制約に気づくことができる。ここでチェックすることはそんなに難しくない。

ロード時に制約を反映させるのは、データ数と実現方法によっては難しい。 「そもそも購入対象外のアイテムは、ユーザーに見せるアイテム一覧に表示させない」というようなことをするのは、結構難易度が上がる。

RDBMSなどのストレージから抽出する際に、SQLで制約を表現しないといけないとすると、いくつか問題が出てくる。まずSQLが複雑になって、可読性が下がり、抽出に時間がかかりうる。

indexの使えない制約をかける必要が出ると、抽出時間は条件やデータ数に左右されてしまうようになり、「どんなケースでも現実的な時間内で処理が完了するか?」を判断するのが難しくなってしまう。

カート追加時、カート購入時と別の場所に同じバリデーションを実装するのも、メンテ性やチェックの難易度を上げる。

ただ、ロードするアイテムをページングして表示しているなら、SQLビジネスロジックを実装せざるを得ない。

ページングしていなくて、表示件数がそんなに多くないなら、カート追加時と同じバリデーションをロードする各アイテムにも適用してから結果を返すという設計もありうる。今回はこのケースに当てはまったので、この方針を採用することにした。(バリデーションに失敗したものを結果から除外した。)

ロード時にバリデーションを行う別の実現方法としては、データロード自体は行うが、カートに追加できないアイテムは、追加ボタンを無効にするという手法もある。この方法ならページングしていても、SQLビジネスロジックを実装しなくても対応できそう。