validatable-recordというモジュールをすこし前に作った。Twitterでは軽く流したが、実際にはこれを作ろうと思ったモチベーションが少なからずあったので、自分自身の学びやのちのちの振り返りのためにも、そのモチベーションについてここに書き残しておきたい。
背景
このモジュールを作っていたとき、僕はチームでVue.jsを用いたSPAの開発をしていて、アプリケーション・フォームのバリデーション・ロジックの実装にとりかかろうとしていた。Vue.jsのバリデーション・モジュールの有名なものにはvue-validatorというものがあるらしく、検索すると上位にヒットし、スターも多い。
作者もVue.js界隈ではよく知られているkazuponさんである。このモジュールのいいところは、モジュール自体の出来もさることながら、バリデーション・ロジックの実装自体を全てビューレイヤで完結させることができ、とてもシンプルに使いやすいところだ。Vue.jsというフレームワークの持つ小さなラーニング・コスト同様に、このモジュールもとても手に馴染みやすい。しかしながら、アプリケーションのアーキテクチャによっては、バリデーションという役割が、このモジュールによって果たされることがよくない場合もある。
バリデーション・ロジックという責務
バリデーションをどこで行うか? という事柄には「常にこれ」という答えがない。ビューでバリデーションをすることもあるし、そうでない場合もある。Railsではビジネスロジックの一部としてモデルレイヤがバリデーションを扱う。このような「バリデーションがビジネスロジックの一部である」という観点はとても興味深い。というのは、例えばフォームのバリデーションには多くの場合ビジネスロジックが関わることが多いからだ。
もちろん、インフラストラクチャ・レイヤの事情によって、ユーザ名を制限したり、文字数制限を課したりする場合もあり、こうしたバリデーションはどちらかと言えばインフラストラクチャ・レイヤの一部だ。だが、別の例としてECアプリケーションにおける特定の商品の注文数の制限などは、限りなくビジネスロジックに近い。なぜなら、これはこのアプリケーションのビジネス固有の事情によって生まれるバリデーションだからだ。
モデル・レイヤ
アプリケーション開発において意識する必要があることは、そのアプリケーションがこれからもスケールする可能性があるということだ。個人で作っていたり、フレームワークの練習で作るアプリケーションはその限りではないが、一般的なアプリケーションは毎日のように変更が施され、複数人の開発者がコードに手を加える。とりわけ、フロントエンド・アプリケーションはビューに大きな変更が加わるケースが多く、その過程でコード内のロジックがビューやビューモデルに偏りがちになることが多い。
レイヤード・アーキテクチャからフロントエンド・アプリケーションを考えると、ビュー/ビューモデルはプレゼンテーション・レイヤの一部であり、そこが多くを知っていることが良い方向に向かうケースは少ない。そのため、アプリケーションから、固有のビジネス・ルールを切り離したドメイン・レイヤがフロントエンド・アプリケーションにも存在しているというのはなんらおかしなことではないと言える。
巷では、様々なエンジニアがFluxアーキテクチャにおいてドメイン・レイヤをどこに置くか、という話題について記事を書いているが、僕のケースでは、Immutable.jsのRecordを用いてストアの一部としてドメイン・レイヤを置いている(僕のアプリケーションは、React+Reduxの技術スタックではないが、以下のスライドの図は参考になる)。
もし、ビュー・レイヤの中にバリデーション・ロジックを置くとしても、それはドメインに固有でないもののほうがよい。ドメインに固有のバリデーション・ロジックは必ずしもビュー・レイヤからの入力だけに使われるとは言い切れないからだ。APIサーバやデータベースなどのインフラストラクチャ・レイヤからのデータの取得もまた、アプリケーションにとっては「入力」となる。
それらがビジネス・ルールに対して適切であるかをチェックするのはドメイン・レイヤにおけるバリデーションの責務だと言える。このような意味でも、フロントエンド・アプリケーションにおけるバリデーションはドメイン・レイヤに置かれるモデルの一部として扱われるのが適切であることが多い。
validatable-record
Immutable.jsのRecordでモデルを定義する際には、最終的にはバリデーション・ロジックは自前で書かねばならない。npmで検索すれば、様々なバリデーション・モジュールがひっかかるので、そこから自前で作ることができる。しかしながら、validatable-recordを使えば、validate.jsのバリデーション定義を組み込んだイミュータブルなRecordを定義することができる。
const ManRecord = ValidatableRecord({ name: null, age: null }, { name: { presence: true }, age: { presence: { message: "is invalid" } } }); // Immtable.jsのRecordと同じように使える const man = new Man({ name: "Justine"; age: 25 }); // もちろんRecordのメソッド群も使える man.size // 2 man.get('name') // "Justine" man.get('age') // 25 // バリデーション man.validate() // == true
設計の指針として、バリデーション・ロジックをどこに置くか、という点を考えておくことは、決して無駄骨になることはないし、息の長いアプリケーションを開発する際には、すこしでも時間をとって考えておきたい。validatable-recordはその中でドメイン・レイヤにおけるバリデーションを実装するとしたらこんな感じかな、という小さなアプローチを提案するものだった。僕らのところではこうしてるよ、こういうものを使ってるよ、などの話もいつかどこかの勉強会で聞けたらいいな、と思う。