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

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

FormとValueObjectの関係を整理する

vermeer.hatenablog.jp

で書けなかったモヤモヤしたところを、自分なりにBeanValidationを中心として考えていく中で整理できつつあるので、具体的な実装に入る前に、まず まとめてみたいと思います。

それは、画面用オブジェクト(以降、Form)とドメインオブジェクトの関係についてです。

具体的には

P210
ドメインオブジェクトをそのまま使うことを重視すべきです

のところです。

ビジネスロジック部分については、そのまま使えることを重視するのは賛成です。

ただ、Formは本質的には「文字列」です。

なぜ「そのまま使うこと」と言えるのだろうと、思ったのですが実装サンプルとなりうるコードを読んでみて分かりました。

isolating-the-domain

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:コードで理解するのは大事だと再認識