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);

まとめ

まとまりません! もっと理想的なバリデーションフレームワークが欲しい!

というわけでバリデーションへの悩みは尽きません。 悩ましい。