システム開発で思うところ

Javaで主にシステム開発をしながら思うところをツラツラを綴る

FormのプロパティをStringに

vermeer.hatenablog.jp

以前の記事では、どちらかというと 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

Bitbucket

さいごに

個人的には、そこそこの納得感を覚えたわけですが、もともとの参考にさせていただいた

GitHub - system-sekkei/isolating-the-domain: Spring Boot : gradle, Spring MVC, Thymeleaf, MyBatis and Spring Security sample

SpringBootによる実装を見てみると、LocalDateをプロパティにして なんだかイイ感じにマッピングされているみたいなんですよね。

そうなると 今回の考察自体が ふんわりしてしまうところがあるわけですが、フレームワークに限定しすぎない設計を考えているわけだから、まぁ良いかな、という感じです。*3

*1:表示ロジックの実装クラスのプロパティの型をValueObjetにしている、というだけでPresentaterとしているので 厳密なところは 違うかもしれません

*2:なので正確なところは分かりません

*3:限定しない方式というつもりはありません。実際、私はJSFで実装している時点で 限定はされているので