で書けなかったモヤモヤしたところを、自分なりにBeanValidationを中心として考えていく中で整理できつつあるので、具体的な実装に入る前に、まず まとめてみたいと思います。
それは、画面用オブジェクト(以降、Form)とドメインオブジェクトの関係についてです。
具体的には
P210
ドメインオブジェクトをそのまま使うことを重視すべきです
のところです。
ビジネスロジック部分については、そのまま使えることを重視するのは賛成です。
ただ、Formは本質的には「文字列」です。
なぜ「そのまま使うこと」と言えるのだろうと、思ったのですが実装サンプルとなりうるコードを読んでみて分かりました。
SpringMVCだとフレームワークでイイ感じにFormとドメインオブジェクトをマッピングしてくれるんですね。
でも、ここで自分が何にモヤモヤしているのか、ということも具体的に分かりました*1。
それは、FormへマッピングをするためにドメインオブジェクトのtoString
メソッドに「Formのための実装」が強制されているということです。
ドメインオブジェクトは「すべての層から独立したものとしたい」と考えている以上、toString
であったとしても そこに具体的なアプリケーション仕様に直結する作法は含みたくありません。
このことについては、APIデザインの極意 Java/NetBeansアーキテクト探究ノート にも書かれています。
もし含むとしたら、それはForm
クラスとして実装すべきだと思います。
では、具体的にコードとしてどうすれば良いでしょうか?
以下に私の考えるイメージを示します。
コードによる表現
画面全体となるFormクラス
画面全体またはリクエスト単位の集合です。
ここでは、必須入力を処理するAnnotationを付与しています。
@FormNotBlank
は「画面上で必須な項目」を意図したものです。
加えてgroups = FormValidation.class
により検証優先度を指定して、@FormNotBlank
でエラーがあったら以降の検証処理をしないようにしています。
あくまで「操作における必須情報」であって、オブジェクト生成におけるNotNullとは別物です。
@Data public class Form { @Valid @FormNotBlank(groups = FormValidation.class) private FormItem item; }
Formの部品
集合の部品となるForm
その主たる関心事であるドメイン(ValueObject)を包含します。
インスタンス化およびgetterの際の型は基本的にString
です。
これはWebであるための物理制約です。
ValueObjectとStringの変換だけでなく、ドメインに直結しない「表現」に関する物理的な実装を このクラスが担います(論理的な情報はValueObjectに実装します)。
ここではValueObjectのプリミティブな情報をValidation用に返却するメソッドを追加しています。
public class FormItem implements FormObject<String> { @Valid private final ValueObject value; public FormItem(String value) { this.value = ValueObject.of(value); } public String value() { return this.value.getValue(); } @Override public String getValidatationValue() { return this.value(); } }
ValueObject
関心事の中心となるドメインの実装です。
(桁チェックしかしていないので、これを「関心事の中心」というのも変ですが)
@Value(staticConstructor = "of") public class ValueObject { @Min(value = 100) private final String value; }
さいごに
デメリットは実装するクラスが増えてしまうことですが、Domain層とPresentation層の役割を物理的に分けることで、それぞれの関心事による実装汚染を防ぐことができると私は考えます。
もちろん、SpringMVCやRailsのように簡易化するための(Easyにするための)仕組みも生産性のことを考えると有効だと思います。
とはいえ、Easyのための仕組みと、設計としてありたい形は別物だと思います。
まずは「設計としてありたい形」と「実際に実現可能か」というところを きちんと整理した上で、それを「Easyにする仕組み」をさらに準備する という流れで色々と考えていきたいと思っています。
具体的な全体像は別記事でBeanValidationとあわせて まとめる予定です。
*1:コードで理解するのは大事だと再認識