以前の記事では、どちらかというと Presenter パターンに近いイメージで実装しています。*1
集合の部品となるForm
その主たる関心事であるドメイン(ValueObject)を包含します。
インスタンス化およびgetterの際の型は基本的にStringです。
これはWebであるための物理制約です。
これは、これで正しいかな、と思っていたのですが Validation を考えた場合、Formのプロパティ自体はString にしておくべきでは? と考えに至りました。
プロパティをStringにする理由
Httpが文字列だから
もしくは、型クラスをプロパティとするためには、どこかで文字列から型クラスへの変換が必要だから、です。
これは、ValueObjectのプロパティが数値である場合、画面から入力した値に対して どうやって(もしくは どこで)数値保証すべき?というのが経緯です。
以前から クライアント側で HTML5であれ JavaScriptであれ 色々と実装するにしても、誰が どこで その検証を保証するのか?というところの落としどころで考えていました。
そこで まず、Formはクライアント入力情報を そのままの保持するもの、という整理を入口として 以降の設計指針を考えることにしました。
保持と検証のタイミングの違い
防御的プログラミングのできうるものであれば こんな考察は不要だとは思うのですが、Webシステムは 基本的に Validationをするにあたって、サーバー側は 何らかの方式で いったん保持した上で値を検証するという方式を取らざるを得ません。
つまり、どんな相手か分からないけど 一回受け入れて(保持して)から、その上で検証をしないといけない という前提があります。
そうすると 単純な落としどころしては「なんでも とりあえず 保持できそうな型」である文字列をFormのプロパティにしてしまうのが無難であるように考えました。
プロトタイプが やりやすい
これは 主たる理由というよりも、副産物なところですが FormのプロパティをStringにしておけば プロトタイプが作りやすいように思います。
HTMLでデザインをして、簡単なプロトタイプをつくってみるか、という際にプロパティを型クラスにすると、ラフにFormを作るのが少々面倒です。
HTMLデザイン→プロトForm→(並行してモデル分析)→FormにValueObjectを割り当て
という感じのことができます。
実装
以前のテスト実装では、ページ全体を表すクラスを Viewとしていましたが、Pageに変更しました。
同じように ページで使用するフィールドの値を保持するクラスを Formから ViewFormに変更しました。
理由はForm
という表現をインターフェースで使うようにしたためです。
ページ
変更前
@Data public class View { @Valid @FormNotBlank(groups = FormValidation.class) private Form item; }
変更後
@Data public class Page { @Valid private ViewForm item; }
Form
変更前
public class Form implements FormObject<String> { @Valid private final ValueObject value; public Form(String value) { this.value = ValueObject.of(value); } public String value() { return this.value.getValue(); } @Override public String getValidatationValue() { return this.value(); } @Override public String toString() { return this.value(); } }
変更後
public class ViewForm implements DefaultForm<ValueObject> { @NotBlank(groups = FormValidation.class) private final String value; public ViewForm(String value) { this.value = value; } public String value() { return this.value; } @Valid @Override public ValueObject getValue() { return ValueObject.of(value); } @Override public String toString() { return this.value(); } }
実装を振り返って考察
参照だけならFormじゃなくても良いかも
参照だけの場合は Presenter パターンや DomainObject(ValueObject)をそのまま使っても良いと思います。
例えば 始めは 更新あり 参照のみ に関係なく通り一遍で Formを使ってデザインをしておいて、E2Eテストを準備した後でリファクタリングをしていくイメージです。
表示メソッドのDefaultをtoStringに
DefaultForm#display
のdefaultは#toString
にしました。
これはSpringMVC
のFormマッピングでtoString
を表示用に使用している「らしい」という知見からです。*2
つまり表示用の実装としてtoString
を実装するので、ついでにdisplayのデフォルトにもしよう、という感じです。
独自の型チェックが無くなる
ValueObjectをプロパティとする実装の場合、PageでNotBlank
用に独自のValidatorを作成していましたが、今回のようにプロパティをStringとしたことで、標準のValidatorを使う実装に変更できました。
これが良いことか悪いことか それは分からないところではありますが、標準実装でカバーできるのであれば それに越したことは無いかな、と。
元の実装であれば「Pageクラス側で 対象のフィールドがNotBlank(必須)である」という文脈になりますが、改修後の文脈では「Pageクラス側で、対象フィールドを使用している」ということしか分からなくなります。 これについては、どちらの文脈が適当なのか、正直 まだ定まっていないところがあります。
Formのベースが直感的に分かりにくい
見ての通り というところはありますが、ValueObjectがプロパティでないということで、直感的にFormクラスが なんのValueObjectの表示用クラスなのか分かりにくいようにも思います。
あえて言えば、クラス宣言をする際の型アノテーションで示しているので どうにか という印象です。
ただ、プロパティによる定義では インターフェースによる強制が無いので 設計指針の強制という観点で考えると、今回の方が良いのかな?とも思うので、まぁ結果良し、という感じです。
参考リンク
decorator, presenter, exhibit という3つの実装パターンについて - pospomeのプログラミング日記
Code
さいごに
個人的には、そこそこの納得感を覚えたわけですが、もともとの参考にさせていただいた
SpringBootによる実装を見てみると、LocalDateをプロパティにして なんだかイイ感じにマッピングされているみたいなんですよね。
そうなると 今回の考察自体が ふんわりしてしまうところがあるわけですが、フレームワークに限定しすぎない設計を考えているわけだから、まぁ良いかな、という感じです。*3