BeanValidationの相関バリデーションとそもそもの話
BeanValidationで相関バリデーションするときに新しいメソッド作って@AssertTrueを使うよ! っていう話があったので、それについて思う事を書きます。
なお、以下はWebアプリケーションでリクエストパラメータをPOJOにマッピングして BeanValidationを行うという流れを想定しています。
@AssertTrueでバリデーション
件の相関バリデーションはたぶんこんな感じです。
public int from;
public int to;
@AssertTrue
public boolean isFromLessThanTo() {
return from < to;
}
私は、これは
- メソッド内にバリデーションロジックを書くので再利用性が低い
- @AssertTrue、そしてbooleanは宣言的ではない
と思っています。
じゃあ、どうすればいいのか
from < to を検証するアノテーションとカスタムバリデータを作りましょう。 そしてそれを付けるメソッドはbooleanではなくタプル(のような何か)を返します。
public int from;
public int to;
@FromLessThanTo
public Pair isFromLessThanTo() {
return new Pair(from, to);
}
こうする事で、
- バリデーションのロジックはカスタムバリデータに閉じ込めたので再利用性高まる
- アノテーションで大小関係があるって分かって宣言的っぽい
となるかと。
そもそも
アノテーションを使ったバリデーションってどうなんでしょうね?
例えば、次のようなコードがあったとします。
@Isbn
public String isbn1;
@Isbn
public String isbn2;
アノテーションを見ればisbn1もisbn2もISBNであるということは分かるのですが、 でもそれってアノテーションの役割じゃなくて型じゃないの?と思うのです。
本来あるべき姿はこんな感じ。
public Isbn isbn1;
public Isbn isbn2;
この場合バリデーションはIsbn型へ変換するときに行うのが良いと思われます。
型よ
基本的にバリデーションは次の順番で行われると思います。
- 必須バリデーション
- フォーマットバリデーション(日付とされる値がyyyy/MM/ddになっているか?みたいなことです)
- 相関バリデーション
必須バリデーションを行うか否かも型で表したい。 Optionalでないものは必須!という感じです。 たぶんそれが良いと思う。
//必須
public Isbn isbn1;
//必須でない
public Optional<Isbn> isbn2;
フォーマットも先述の通り型で表す事ができます。
それから相関バリデーションですが、最初の例であればRangeといった型を作ってそこにfromとtoを 詰め込めばfromとtoの大小関係を型で表す事ができます。
new Range(from, to);
まとめ
まとまりません! もっと理想的なバリデーションフレームワークが欲しい!
というわけでバリデーションへの悩みは尽きません。 悩ましい。