の続き
今更ですが 特に明示はしていませんでしたが メッセージとは エラーメッセージです。
順序はPresentation層で
表示要件はDomainの主たる関心事というよりも 各ページ毎の仕様です*1。
メッセージの出力順序を制御するのであれば、Presentation層で制御すべきでしょう。
もっと言えば、それ以外の層で順序を制御すべきではなく、Presentation層だけで制御すべきだと考えます。
ありがちなところでは SQLの ORDER BYで 表示順を制御する ということをしていないでしょうか? *2
やりやすいかもしれませんが、表示順という仕様が別の層に漏れ出しています。
(この例だと、Infrastructure層に漏れている)
メッセージの優先度
優先度というのは以下のようなケースを想定しています。
名前は10文字以内です。 回線が混雑しているため、しばらく待って操作してください。
こんなメッセージが出力をしてはいけません。
この場合は、「回線が混雑して……」とメッセージ出力したエラーページに遷移するか、操作画面上のメッセージとして「回線が混雑して……」だけを出力するべきです。
少なくとも、項目検証不明のメッセージを混ぜるのはNGです。
発生したエラーに関連するメッセージを単純に かき集めて出力するだけという訳にはいきません。
きちんと優先度を考慮した仕組みを設けるだけでなく、優先度を指定できる仕組み またはルールも あわせて準備しないといけないでしょう。
優先度の順位付けは、パッと考えると単純ですが 似て非なるものであったり 要件次第で変わったりもするように思うので、意外と面倒かもしれません。
表示項目と表示順序
画面の表示項目順と揃っていないと可読性の悪いメッセージに感じてしまいます。
したがって、まず基本的な表示順序に影響するのは 表示項目順(レイアウト)ということになります。
表示項目で集約した上で、詳細メッセージの粒度も併せたいところです。
全てのメッセージ詳細の粒度を一致させるのは 項目が2つくらいならできるかもしれませんが、3つ以上になると 妥協点を考えないといけません。
例えば、
名前は10文字以内です。 名前に絵文字は使えません。 住所は40文字以内です。 住所に絵文字は使えません。 住所に半角文字は使えません。 // 住所にだけあるメッセージ
こうだと問題無さそう。
名前は10文字以内です。 住所は40文字以内です。 名前に絵文字は使えません。 住所に絵文字は使えません。 住所に半角文字は使えません。 // 住所にだけあるメッセージ
これだと、読みにくい。
名前は10文字以内です。 名前に絵文字は使えません。 住所は40文字以内です。 住所に半角文字は使えません。 // 住所にだけあるメッセージ 住所に絵文字は使えません。
これは許容範囲としたい。
という感じです。
ぱっと見たら、表示項目を一番前したようなメッセージにしておいて、詳細は自然順ソートをすれば 良さそうも思えますが、実際の画面レイアウトによる表示順序が関係するので、そこまで単純ではありません。
また、そのようなメッセージ順序は言語が異なると不自然な場合があるかもしれません。*3
いずれにしても、表示項目順にあわせた並べ替えをすることは必要です。
なかなか面倒くさそうです。
表示項目に直接付与
表示項目に関するフィールドに直接メッセージを関連付けて表現するやり方です。
名前【 】 名前は入力必須です
のような感じ。
この場合、項目に対して複数メッセージを出力することは、あまり良いデザインではないように思います。
そこをあえて対応したとしたら
名前【 】名前は10文字以内です。 名前に絵文字は使えません。 住所【 】住所は40文字以内です。 住所に絵文字は使えません。 住所に半角文字は使えません。 // 住所にだけあるメッセージ
もしくは、明示的に分かっているラベルは外して
名前【 】10文字以内です。 絵文字は使えません。 住所【 】40文字以内です。 絵文字は使えません。 半角文字は使えません。 // 住所にだけあるメッセージ
という感じでしょうか。
横じゃなくて、下というパターンもあるでしょう。
名前【 】 10文字以内です。 絵文字は使えません。 住所【 】 40文字以内です。 絵文字は使えません。 半角文字は使えません。 // 住所にだけあるメッセージ
こんな感じ。
いずれにしても、表示項目によるグルーピング と メッセージ詳細の順番 について考慮は必要そうだということは確実です。
仕組み的なところをいうと、より具体的な位置までマッピングする必要がありそうです。
これは テーブルレイアウトにおける、セル毎のツールチップ表示も 同じ考え方で良いでしょう。
画面レイアウトの下部に、メッセージ一覧を表示するよりも、さらに面倒くさいですね。*4
画面の構造と表示順序
順序に関する情報は、Formクラスではなく ページ全体を示すPageクラス もしくは レスポンスクラスのプロパティに、検証不正を関連付けるべきでしょう。
Formクラスは あくまで画面を構成する部品の型だからです。
複数のコンポーネントで構成されている場合は最上位となるクラス(基本はPageクラス)に順序を示して ツリー構造で表示順序を構造化するイメージです。
あとは、過去の考察の通りです。
Presentation層以外で発生したエラーを、どうやって関連付けるのか、もしくは割り切っていくのか というところが難しそうです。
WebAPIだったら順序は不要?
WebAPI(RestAPI)のレスポンスに付与するエラー詳細にも順序は必要でしょうか?
SPAなどのクライアントレンダリングに使用する場合は必要で、そうではない場合は不要であると考えます。
ただ この切り分けをするとWebAPIの良さが軽減してしまいます。
そうなると 基本的にWebAPIでも順序を考慮すべきでしょう。
まとめ
メッセージ出力順序を 実現するにあたって必要な要件をまとめると
- 優先度を考慮したハンドリング
- 画面表示に応じた出力順の指定
- 画面項目とエラーの関連付け
- エラー起因のメッセージの順序付け
を満たす仕組みを考える、ということでしょうか。
結構 きついな……
過去の経験としては、
- 要件自体がシンプルなので仕組みにあわせて実装をする
- 仕組みを一切使わず 泥臭くControllerにガシガシとスクリプト的な実装をする
の2択がメインでした。
なお、オレオレライブラリで対応した際は、レイヤーを跨って強依存な情報を持たせるような仕組みを作ったのですが*5、 DDDとか学ぶ過程で得た発想と反省(?)から、今回は、もうちょっと ちゃんとしたものにしようと考え至りました。
多分、今回も何かしらの割り切りをすることになるでしょう。
できれば 宣言的な実装と スクリプト的な実装を ほどよくミックスできる柔軟な仕組みを考えたいとは思っていますが……
その他
実行時例外以外の表現
参考資料の中のScalaに関する資料をみて、Eitherとか 面白い考え方だな と思いました。
実行時例外という副作用について 仕組みを分かっていれば問題ありませんが、何も知らない 新しく参画したメンバーした際、そういった暗黙知ベースなところから フレームワークの本来の設計思想が綻びていく切欠になるというのは良くある話です。
従来のリターンコードと同じだと思って 始めは興味が無かったのですが、戻り値に対して統一的な型を持たせておくという設計(実装)思想は 似て非なるものだと思うようになりました。
チェック例外にも似ていますが、やっぱり違っていて 呼出し元への強制が弱いというか、統一した型を用いるというところだけというのが良さそうです。
Javaで Optionalを使うのと ほとんど同じ感覚で 例外に準ずるものを明示的に扱えるのは良いように思います。
より具体的には、@makingさんのYAVI*6 で Either を使ったライブラリの使用事例をみて理解が深まったという感じです。
とはいえ、まだ理解の乏しく、JavaでEitherを使った仕組みを作りこむよりも、例外を用いた実装の方がJavaの言語仕様として馴染まれている*7ということで、いったん「面白い考え方だ」というところで留めておきたいと思います。
参考資料
DDDの仕様パターン - pospomeのプログラミング日記
WebAPIでバリデーションチェックエラーの際、HTTPステータスは何を返すのが適切か
www.slideshare.net
エラーメッセージはフォームのどこに表示するべきか | UX MILK
さいごに
色々と整理したわけですが、当然 現時点で できているわけではありません。
これから、そこを目指そうというところを整理したということです。
さて、実際に実装していく中で どれくらい見直さないといけなくなるのか……
恐ろしくもあり、楽しみでもあり。*8