これみなさんが悪いわけじゃないと思うんですよ。そもそもビジネスロジックということばで指してるものがみんな違ったりするんですよね。そんな状態で、「ビジネスロジック」って言葉を使っちゃって名前がついちゃうと「なんだかわかったつもり」になったまま実はなにもわかってない、みたいなことになりがちなんですよね。今日は「ビジネスロジック」って言葉忘れちゃった方がいいと思います。何が言いたいかっていうと、「モデルとは何か」ってのを言い表すのはすごい難しいんですよ。いうわけでですね、わからないときは分かりやすいところから考えましょう。まあViewはわかりやすいですよね。iOSだったらUIViewがあるし、AndroidにもViewがある。webAppだったらHTMLとCSSで表現されるのがViewですよね。実はWの部分もわかりやすくて、こういうやつらです。これらが何を担っているかっていうと、ViewをレンダリングしたりViewに値突っ込んだり、あるいはユーザーからの入力を受け取るみたいなこともやりますよね。interface)」っていってますよね。UIって見た目だけじゃなくてユーザーがボタン押したら云々みたいなユーザーインタラクションも含むものだってのは開発者のみなさんはよくご存知だと思うんですけど、それと照らし合わせて考えると、VとWの担ってる責務こそが「プレゼンテーション」であるってことなんですよね。VとWでプレゼンテーションを実現するわけです。WにはCとかPとかVMとかいろんなものが入ってくるんですけど、これは要するに「どうやってプレゼンテーション層を書くか」のやり方をいろいろパターン化してくれてるわけですね。残りの、MVWでいうところModel、PDSでいうところのDomainっていうのはどんな責務を担当するんだろう、って話についに切り込んでいきます。PDSにみたび登場してもらうとですね、赤字で書いたところ注目してください。rest」って書いてありますよね。結局なんなのかわかんない!って感じですよね。それは当たり前なんですよ。そもそもPDSってのは、「プレゼンテーション層とそのほかを分けよう」って考え方だし、この「その他」が何を担うべきなのかってのはアプリケーションによって変わるわけです。これ大事なことなので何度も強調しますが、そもそもMVWは「プレゼンテーション層をどう設計するか」のパターンであって、Modelをどう設計するべきかについては何も語っていないパターンなんですね。はい、ここまでがアニメとかでいうところのアバンパートです。いうわけでオープニング、タイトルコールどん!Aパートはい、こういうタイトルでしたね。MVWはModelの設計指針についてなにも語っていないことは確認できました。すると次の疑問として「じゃあModelはどうすんのよ、なんの指針もなしにどうやって設計すんの」って話が出てきます。いうときに力になるのが様々なデザインパターンです。デザインって設計のことですよね。いうパターンをたくさん知ることで、「ははーんこれはxxパターンを適用すべき事案ですね?」とか見えてくるようになるわけです。今日はそんな中でみっつほどパターンを紹介しましょう。ActiveRecordパターンです。これRailsのおかげでみなさんも結構よく知ってると思うんですけど、こういうパターンですよね。Railsで使われてるActiveRecordライブラリを使えば自動的にARパターンが実現できるわけではないんですよ。いうコード見てみましょう。継承したNotificationから、unreadなものを引いてきて、それを全部readに更新してます。read」ってやつですね。ところで、ControllerってPDSでいうとPとDどっちに属すやつでしたっけ?プレゼンテーション層ですよね。この「DBからレコード引いてきて更新する」って、全然ユーザーインタラクションじゃないですよね。そこで、ARパターンを適用してみます。はい、テーブルに対する操作なのでクラスメソッドとして実装しました。(あっ、これActiveRecord::Base継承しわすれてますね、してると思って読んでください)注目すべきはcontrollerです。これ、ユーザーからリクエスト、つまり入力を受け取って、Modelに「あとはまかせた!」ってやってますよね。これはユーザインタラクションの実装で、プレゼンテーション層の責務の範囲に収まってると言っていいと思います。したことによって、なにが嬉しいかというとですね、実はプレゼンテーション層ってのはだいたいテストしにくいんですよね。RailsだったらRack経由でテストしないとダメだし、iOSアプリとかだとViewとかViewControllerが絡むと一気にテストしにくくなりますよね。Cの責務をユーザーインタラクションだけに絞ったことで、あとはRackとかそういうテストしにくいところ経由しないでテストが書けるようになりました。あと、もう一個いいことがあります。Cにいろいろ書かれちゃうとそこ再利用するの難しいんですけど、こうやってmodelに書いておけば、controllerからだけじゃなくてrakeタスクとかからも呼べるようになりますよね。これって、「プレゼンテーション層を入れ替えてる」ってことなんですよ。このアプリケーションを使うクライアントからみると、RailsのViewとかControllerで実現してるのってHTTPを喋るプレゼンテーション層なんですよね。一方で、CLIのインターフェイスを実現するrakeタスクなんかからこのModelを使えば、rakeタスクの層が「CLIを喋るプレゼンテーション層」になるってわけです。やってプレゼンテーション層をぽこぽこ入れ替えることができるのも、PDSに沿うことで得られる強みですね。いえ、現実は厳しくて、「これでめでたしめでたしやで!」とはなかなか行かないんですね。ある課題としては、複数のテーブル触るようなユースケースをどっちに書くのか問題ってのがありますよね。ARを継承したモデルの中でトランザクション開始しちゃうと、そのメソッドをほかのところからも使おうとすると多重にトランザクションが開始されちゃってうわあみたいな感じになったり、みなさんも経験ありませんか?そういうのむりしてActiveRecordパターンで扱おうとしていろんなところにぐちゃーっと詰め込んでいくと、まあメンテ不可能なRailsアプリが出来上がるわけですね。はい、お疲れ様でした。何がいけなかったのか考えてみましょう。そもそもActiveRecordパターンってのはどんなパターンでしたっけ?「テーブルと」クラスが1:1なんですよね。そもそもテーブルとクラスが1:1なんだから、複数テーブルにまたがった関心を扱うには「食い合わせのわるい」パターンであるってことが言えるんじゃないでしょうか。実際、実践DDDっていう最高の本があるんですけど、この中でも「単なるCRUDアプリならRailsでいいかもね」って言ってて、要するにActiveRecordパターンが対象にするのってCRUDの範囲だけなんですよね。ARパターンだけでこの世のすべての問題を解決しよう、というのは無理があるわけです。ちなみになんですけど、RubyのARライブラリってARパターンをもとにしたライブラリなんですけど、単なるARパターンじゃなくてもっといろんなことやってくれるんですよね。RoRの界隈ではARライブラリベースでやっていこうという考え方もあるにはあるっぽいんですけど、あまりこれでうまくいったという話を聞かないような気がします。逆にARライブラリベースで上記みたいな問題を解決したぜっていうのは興味ある話なので、そういう話知ってる人はぜひ教えてください。皮肉とかじゃなくてまじで聞きたいです。ええと、話を戻して、ARパターンですべての問題が解決できるわけではないということを見てきました。そこで、別のパターンとしてTransactionScript(以下TS)ってのを見てみましょう。TSってのはユースケースと一対一になるメソッド定義して、そのメソッドの中に全部手続きを書いていくやつです。航空機の座席予約について考えてみましょう。その席が誰かに先に予約されてないかチェックしてビジネスクラスの料金でファーストクラス予約しようとしてないかチェックして場合によってはパスポートのvalidityチェックしてとか、いろいろとやるべきことはあるわけです。それを全部上から下に書き下していくスタイルですね。これを適用すると、こんな感じのコードが出来上がってきます。いろんなモデルを使って、ユースケースをがんがん書いていくわけですね。トランザクションもこのメソッドの中で開始してcommitすればいいです。ちなみに、これはTSとARの合わせ技になってますね。ここでもcontrollerに注目してください。入力を受け取ってTSのメソッドをdispatchしてるだけです。TransactionSctiptパターンで作ったこのNyanTransactionクラスもModelの一種であることに気をつけて下さいね。再度確認しますが、Modelというのは「プレゼンテーション以外全部」なのだから、当然ARを継承してないこのNyanTransactionもModel層のものです。この場合もCは入力を受け取ってMの処理をdispatchしてるだけなわけです。いうふうにすると何がうれしいのかって話なんですけど、ARだけでは実現できてなかった複数の関心をうまくまとめられてるし、トランザクションもここに書いておけばバッチリ良さそうですね。いえやっぱり現実は厳しくて、まあ一定以上複雑なアプリケーションをTSパターンでとこうと思うと、似たようなロジックがあちこちにコピペされて死んだり、ひとつのメソッドの行数数えたら10億行あってそれ読むのに5億年かかるしもはやテスト不能みたいな状態になるんですよね。はいお疲れ様でした、という感じです。いえですね、そこまで複雑でないアプリケーションならば、TSってすごく見通しがいいんですよ。どっかがバグったら、そのユースケースと1:1になってるメソッド読みに行けばいいって一瞬でわかるし、そもそもそのメソッドも「あれしてこれしてそれする」ってことしか書いてないから、複雑じゃなかったらむしろ読みやすいんですよ。あと、余計な設計しないで済むんで、初期開発速度も速いわけです。なってくると、たとえば「とにかくバギーでもいいから早くリリースしてくれ!そんである程度までユーザー数伸びたらどっかに売ってあとメンテしてもらおう!そんで遊んでくらそう!」みたいなビジネス要請がある場合にはまあ倫理的にどうかって問題はあるにせよ、TSでドバーって書いちゃうほうがよかったりするんですよね。一方で、「複雑な内容だから急がないんだけど、ずっと直しながら使っていくシステムをしっかり作ってほしい」っていうようなやつをTSで設計したらちょっとまずいわけですよね。そんな感じで、モデルをどう設計するのかってのは実は「どういうビジネス要請があるのか」みたいなところと深く関わっているわけですよ。モデルを設計するときには各パターンのメリットデメリットを知って、そしてそのパターンが何を解決しようとしているのかを知って、その上で「こういう要請があるから今回はこのパターンを使っていこう」というように設計を進めていくべきなんですね。「TSはクソ、時代は○○」みたいな話ではないんだよ、ってのを強調しておこうと思います。まあとはいえですね、TSが適さない問題領域、ビジネス要請っていうのはあるわけですよね。複雑でなおかつ堅牢でなければならないみたいな。Architcture(以下LA)を検討してみたらどうでしょうか。いうものかっていうと、こんな感じです。プレゼンテーション層はいいですよね、その下が全部モデルになるわけですけど、この層を、みっつに分けます、まずはCがdiapatchする窓口となるApplicationServiceっていう層があって、あとDBアクセスとかそういう汎用的な技術を実現するレイヤーをInfrastructureって定義して、それ以外をすべてDomainModelという層にやってもらう。これ何がいいかっていうと、InfrastructureがDBアクセスとかやってくれるから、DomainModelはほんとに解きたい問題に集中できるんですよ。タスク管理システムを考えてみましょう。そのタスク管理システムは、一回だれかがアサインされたタスクに関しては二度と「だれもアサインされた状態」には戻せないってことにしましょう。これはマネージャーはだれかをアサインしたと思って安心してたらだれかそれをさし戻しちゃって、誰も誰がボールを持っているのかわかんない宙ぶらりんのままのタスクができるのを避けるためです。あと、一回クローズされたらもう二度とオープンできないようにしちゃいましょう。「一回クローズしたのは終わったんだからそれは終わったものとして扱う!!!再オープンしたい?それは修正ではなくて変更です!新しいチケットを切ってください!」ってことですね。いう「このアプリケーションが解きたい問題領域」ってのをDomainModelにOOでコードでモデリングしていきます。そんで、さっき定義したTaskStatusを利用するTaskクラスをこんな感じで定義してみましょう。このシステムではタスクはマネージャしかオープンできません。権威主義的で最悪のサービスですけど、まあ例として許してください。いう知識をTask.openメソッドの中にこうやって書いていきます。あとはまあ普通にオブジェクト作って返してますね。Taskをだれかにアサインするメソッドみてください。権威主義的で最悪のサービスなんですけど、だれかにタスクをアサインできるのはマネージャだけです。あと、Taskの状態遷移はできる遷移とできない遷移がありましたよね?それをモデリングしたTaskStatusを利用して、「ちゃんとこれは許された遷移かな?」とかをチェックしたりしてます。ここで注目してほしいのは、TaskとかTaskStautsみたいなDomainModelに定義されてるクラスは、自分がどこに永続化されてるとか気にしてないことです。いう技術的な詳細を避け、「複雑な問題」に集中して丁寧にOOで設計して責務分割していくわけです。やって丁寧に責務分割したとしても、オブジェクトがメモリに乗ってるだけではまああまり意味ないですね。永続化はどうすんの?という話があります。今回はリポジトリーパターンってのを使ってみましょう。リポジトリパターンってのはどういうやつかっていうと、なんちゃらリポジトリにDomainModelで定義したなんらかのオブジェクトを渡して永続化してもらったり、永続化されてるところから情報引っ張ってきてそれを使ってDomainModelで定義したクラスのインスタンスを返してくれたりするようにするパターンです。今回だったらTaskをRDBに保存するTaskRepositoryってのを定義してみました。ApplicationServiceがDomainModelとInfrastructureを利用して、ユースケースを実現します。ここに定義したメソッドがControllerとかからdispatchされるわけですね。いうパターンを利用することで、TSのときは一枚岩でメンテ不可能だったような複雑な問題を適切に分割して統治することができそうです。一枚岩のスクリプトをテストするのは非常に困難ですが、今回はDomainModelをきちんと設計しているので、単体テストも非常にしやすいですね。あと、DBアクセスをInfrastructureに追いやったことで、複雑な問題を解く部分、すなわちDomainModelの層はDBなしでテストできるようになりました。いえやっぱり銀の弾丸はないわけで、これって非常に難しいんですよね。そもそもLAに慣れてないプログラマにとっては「これどの層に書けばいいの?ApplicationService?DomainModel?」みたいな話になりがちです。それだけならともかく、OOって本質的に難しいと思うんですよ。さっき私が書いたTaskStatusとTaskの責務分割もほんとにあれでよかったんですかね?遷移先を渡すとそこに遷移できるかどうかをboolで返すんじゃなくて、TaskStatus#transit_to_assignedみたいなメソッドにして、そいつが繊維先の状態であるTask::Assingedのインスタンス返すようにする。そんで、もし行けないステータスに行こうとしてたら例外吐くみたいなほうが「チェック忘れ」とかなくなるしいい設計なのでは?そんなわけで、とにかくやっぱりOOって難しいんですよ。するとドメイン層に関してはプログラマとしてのレベルを上げて物理で殴るとか、あるいは複雑な問題領域に対する深い理解とかが必要になってくるんですよね。ほんとにつらいですね。はい、お疲れ様でした。いえですよ、われわれはもともと「複雑な問題」に立ち向かうためにここまでやってきたわけです。そもそも難しい問題を相手にしているのだから、そこはがんばって、歯を食いしばってやっていくしかないんですよね。いう複雑な問題を解くときにこそ、OOへの深い理解とか、あるいはDDDへの理解みたいなのが役にたつのだと私は思います。ちなみにFAQなんですけど、聞きかじりでこういう話を「これって要するにDDDでしょ?」っていう向きもあるんですけど、DDDとLAは同一視しないほうがいいです。DDDの中でLAに触れることはありますが、DDDはそれだけではなくてもっといろんな概念を含んだ、より広い概念です。LAはおまけというか、DDDを生かすための前提みたいなところがあると思います。単なるLAをDDDと呼んだりするとDDDの怖いひとにはてブとかTwitterで怒られるみたいな光景をよく目にしますので、気をつけてやっていきましょう。はい、ようやくAパート終了です。いうわけでアイキャッチどん!あー、かわいいですね。Bパート前のアイキャッチもどん!あー、かわいいですね。不審なお兄さんもいますね。Bパートはい、つづいてBパートです。Aパート結構概念的な話多かったんで、GUIアプリを例に実際になにかを作ってみましょう。いう連打マシーンはどうでしょう。ボタンをタップすればカウンタが1進んでいくのでガンガン連打してくれ!!ってやつです。ひとりでやってても面白くないので、ネットワーク越しにいろんな人が同時に連打します。手元のカウンタには自分の連打数じゃなくて、「みんなでどれだけ連打したか」が表示されます。実装方針を考えてみますが、ボタンを一回タップしたごとにHTTPリクエストとか飛んだらまじで目も当てられないという感じなので、クライアント側では連打をバッファリングしておいて、一定時間ごとにそれをflushするようにしましょう。いえ、タップした瞬間にカウンタが動かないとUXとしては最悪なんで、タップすると、「実際にはまだサーバに送ってないんだけど、見た目の都合でカウンタは1増える」というようにしましょう。あとは、サーバーから定期的に「現在のみんなの総連打数はこれだよ」って送られてきて、それを受け取ったらカウンタをその数字まで進める、という実装方針でいきましょう。単なる連打マシンなんですけど、実装がちょっと複雑になりそうですね。そもそもRDB関係ないアプリなんでARパターンはマッチしなそうです。バッファリングとかいろいろあるからTSもマッチしなそうですね。LAっぽく考えてみましょう。Modelを考えていきます。なにはともあれ、カウンターを表すクラスが必要ですよね。タップされたときは1増えるのでincrementメソッド生やしておきましょう。あと、サーバーから数字送られてきたらそこまでいっきに飛ばないとだめなんで、setCountメソッドも生やしておきましょう。こいつらが内部に保持したcountを操作します。はい、こんどはタップ数をバッファしてくれる君です。タップされたらカウントをインクリメントして、flushメソッドが呼ばれたらflushします。今回はプレゼンの時間の都合上サーバーにアクセスする部分は省略しますが、本来ならもっと真面目に書かないとダメです。あとはサーバーから送られてくる値をさっき作ったカウンターにセットしてあげる君ですね。connectToServerすると、一定時間おきにサーバーから値が送られてきて、それをcounterにセットしてます。これもプレゼンの時間の都合上バッサリ実装カットしてますが、もっとまじめに書かないと本来はダメです。あとは、これらを束ねてユースケースを実現するApplicationServiceを書きましょう。プレゼンテーション層からconnectToServerが呼ばれたらfetcherを起動します。起動しておけばあとは勝手にcounterがアップデートされます。これはコンストラクタに書いてもいいかもですね。ボタンがタップされたらこのtapメソッドを呼びます。すれば、カウンタは1増えるしバッファもされます。あとは、プレゼンテーション層が一定時間おきにflushBufferメソッドを呼んであげれば、サーバーに値が送信されます。これでアプリケーションの挙動はモデリングできました。こんどはプレゼンテーション層を書いていきましょう。こんな感じで、Viewがロードされたら、さっき作ったapplicationServiceをセットアップします。あとはタイマーイベント仕込んで、定期的にapplicationServiceのメソッドを叩いてあげます。tapされたというイベントが起こったらapplicationServiceのtapメソッドをdispatchしてあげましょう。タイマーイベントってここに書くの?という疑問も上がりそうですが、タイマーってアプリケーションに対する一種の入力だと考えることができると思いませんか?そう考えるとPresentation層にあるのが自然な気がしますね。あとこのほうがModelのテストしやすい。こんな感じで、プレゼンテーションからイベントを受け取ってapplicaitonServieをinvokeするところまでできました。これひとつ問題があって、これだけだとタップしてもタイマーイベントが走ってもサーバーから値が降ってきても、Viewが描き変わらないんですね。内部でModelの状態が変わりまくっていくだけで、画面が一切動かない。考えると、今回はCounterモデルの値が書き換わったときに、それに合わせてUILabelも書き換えればそれだけでよさそうです。なんかの状態が変わったことを検知して、なにかをしたい。Observerパターンでやったところですね!というわけで、CounterをObservableにしてあげましょう。値が書き換わったら、notifyメソッドを読んで、自分を監視してるひとたちに状態を通知してあげるわけです。それに対して、VC側ではこのCounterをobserveして、状態変化を検知して、その結果をUILabelに反映するようにします。これでひとまず完成です。これ、Observerパターンを利用してModelの状態をViewControllerに開示したことが結構ポイントです。CounterモデルがViewControllerやUIViewを保持して、Counterの値が書き換わったときにそれらのメソッドを読んでViewControllerやViewに変更を伝える形にしてしまうと、CounterがViewModelやViewに依存してしまします。CounterをテストしようとしたときにViewControllerやViewが絡んできて、めっちゃテストしにくくなりますよね。Observerパターンを使えば、「Modelの値や変更をPresentationに開示はするが、Presentationに依存しない」という状態を保つことができます。Foundationしてるだけでしたよね。あとですね、モデル層がCocoaTouchフレームワークに依存しなくなったことで、モデル層は熟練のiOSアプリプログラマ以外にも読めるし書けるような内容になってますよね。これっていいことですよね。レビュー可能な人間が増えるし、分業もしやすいし。実際さっきの連打アプリのモデル層、iOSアプリ書けない人でも内容は理解できたと思います。実際ファウラー先生も、冒頭で紹介した記事でこんなこと書いてて、「あー、PDSが実現できて最高だね、よかったよかった」って感じですね。いうわけで感動のエンドロールにいきましょう。thanksって書かれてるのはプレゼンの練習に付き合ってくれた友人たちです。いやー、よかった、とか思ってたらですね。ディレクターから割り込みが入るわけですよ。エンドロール止めましょう。なにかっていうと、今の実装だと、サーバーから数字が送られてきたときにカウンタの数字が4から10000とかにいきなり飛んだりするわけですよ。それは「間違い」とか「バグ」とはいえないんだけど、最悪のUXだし、まあユーザはびっくりして「これバグじゃん」って思っちゃいますよね。たしかによくない、でもディレクタさん、あなたはプロなんだから「バグ」じゃなくて「よくないUI」くらいに言ってくれ、たのむ、という感じですね。そんな感じで、現実はあまくないですね、エンドロールなんて見てないでまだ仕事しないとダメそうです。いえ、やっぱりいいアプリ作りたいんで、UIを改善しましょう。これってUIの話なんで、プレゼンテーション層でやっていきますよ。UILabelをサブクラッシングしたカスタムなCounterLabelってのを定義します。これは内部に「ほんとのカウント」と「今表示してるカウント」を持ってて、ほんとのカウントがセットされたらタイマー起動して表示してるカウントのほうをタイマーで1ずつupdateしていきます。表示してるカウントがほんとのカウントにおいついたらタイマー止めます。これ、ロジックですよね。「どう見せるか」っていう関心なんで、Presentationの関心ですよね。いわゆる「プレゼンテーションロジック」です。これはViewに閉じ込めてしまいましょう。VCで直接UILabelに値をセットしてた部分も、この変更に合わせて変更しておきましょう。これで、サーバーから値が送られてきたときにはいきなり数字が飛ぶんじゃなくて、見た目上はすごい勢いで数字が増えてるみたいに見えるようになったでしょう。めでたしめでたしですね。話がずれますが、今回はViewにプレゼンテーションロジックを書きました。MVPならPresenterにこういうロジック書けば、そこがテスタブルになっていいですね。MVPにするのがさっきみたいな古典的MVCにするのかはともかく、今回、UI上の問題を解決するときにはPに手を入れただけで、アプリケーションの本質的な問題をモデリングしたDには一切手が入らなかったことに注目してください。こんな感じで、PDSを守っているといろいろいいことがありますね。いえ、実はこのアプリもまだまだぜんぜん完璧ではありません。今回ってサーバーから送られてきた数字をカウンタにセットする君をModelに書いちゃいましたけど、これって本当にそれでよかったんですかね......?タイマーイベントがアプリケーションへの入力だと見なすのが自然なように、サーバーからの入力もアプリケーションへの入力であると見なすのが自然なのではないでしょうか。するとこれはプレゼンテーション層で管理すべき関心の気がします。それを考えると、プレゼンテーション層にもう一個、ActionProviderみたいな層があってもいいかもしれませんね。ここには、タイマーイベント発行してくれる君だとか、サーバーからpushしてくる値を受け取ってイベントを発行してくれる君みたいなやつを定義しておいて、VCがこいつらを利用してモデルに処理をdispatchしたほうがいいかもしれません。あと、今回Observerパターンを結構重要な要素として使っていますが、実はObserverパターンってmutableな世界が前提のパターンなんですよね。それは当たり前で、変化しないものは監視する必要がないからです。言われるように状態のmutationは問題を複雑にします。最近はだから「なるべくimmtableに寄せていこうぜ」というのがトレンドになってきてますよね。それを考えるとObserverパターンを素朴に使ってるのはまだまだ改善の余地がありそうに見えます。あと、今回API呼び出しダミー実装でDomainModelに置いちゃいましたが、API呼び出しって本来Infrastructureがもつべき関心ですよね。DBとのやりとりと同じく、外部システムとのやりとりっていう技術的な詳細ですし。考えるとやっぱりAPI呼び出しの部分はInfrastrucreに持つべきなんじゃないかな、とかいろいろ考えるわけです。あと、これは今回のGUIアプリの例とはあまり関係ないんですけど、Repositoryパターンってクエリと相性最悪なんですよね。「いろんなテーブルをjoinした上で特定のカラムしか必要ない」みたいなやつって、DomainModelで定義したオブジェクトに綺麗にマッピングできないことが多いです。あと、クエリの種類によってRepositoryにファインダーメソッド定義しまくっていくと、異常な量のメソッドがRepositoryに生えてきて破滅します。テーブルと1:1で設計できたARのときはまあよかったんですけど、Repositoryパターンを使ったことで、ここにきていわゆるOOとRDBの間のインピーダンスミスマッチ問題に悩まされるようになったわけです。いうことを考え始めると、いろんなことが気になってきます。さっきのActionProviderとか、immutableな設計とか考えると「おっこれってFluxアーキテクチャに近づいて行っていないか?いっそFluxにするべきか?」とか、「リポジトリパターンでうまくクエリが扱えないなら、コマンドとクエリを別にモデリングしちゃうか、あ、それってCQRSだよな」とか「じゃあいっそEventSourcingまで持って行ってしまうか?」とか、まあいろいろと本当に考えることが増えてくるんですね。これは完全に設計沼です。いうときには、「わたしたちはどんなビジネス要請によってアプリケーションを開発していて、このアプリケーションは何を解決するためのものなのか」ということに立ち返ってみましょう。単なるCRUDアプリケーションに対してCQRSを適用するなんて馬鹿げた話です。今からとても大事なことを言いますが、世の中には「最高の設計」なんてなくて、問題や課題に対する「最適な設計」があるだけなんです。最初の話に戻りますが、「万能なモデルの正しい設計」なんてないんです。そんなわけで、ここから先は、みなさんの作るアプリケーションがどのような課題に対するものなのかによって物語が変わってきます。「ここからさきは君自身の目で確かめる」しかないわけです。つらいですが、ここで唐突にゼルダの伝説に出てくるトライフォースの話をしましょう。トライフォースはみっつに分かれているのですが、そのみっつとは、力のトライフォース、知恵のトライフォース、勇気のトライフォースです。様々なパターンを知り、そのパターンのメリット、デメリット、そして何を解決するパターンなのかを知れば、それはあなたの力になります。力のトライフォースですね。力があるだけではダメで、今度は課題に対してどのようにその力を使っていくのかを知恵を絞って考える必要があります。これが知恵のトライフォースです。いって、複雑な現実に立ち向かうのはかなり厳しいです。勇気を持って、力を知恵を運用することで初めて、良い問題解決が生まれるのではないでしょうか。PDSを心がけることでいいことがたくさんありますMVWはPについては設計指針をくれますMについては設計指針をくれませんMについてはあなたがどのような課題に向き合っているのかによって適切な設計が変わってきます。大変な旅になりますが、トライフォースがあなたの力になるでしょう。最後に宣伝させてください。わたしが働いている株式会社リラクでは、普段からこういう話をプログラマ同士でやりながら開発を進めています。みなさんの会社もそうかもしれませんが、正直いって無限にやるべき仕事あるので、仲間が欲しいです。一緒に、課題に対して最も適切な設計を追求していきたい仲間を募集しますので、「あっなんだか雇用が流動化しそうになってきた」みたいな方は是非声をかけてください。一緒に悩みながらやっていきましょう。宣伝で最後終わるのちょっといやなんで、またまとめのスライド写して、トークを終えさせていただきます。ご清聴ありがとうございました。「そのクエリとARパターンの相性がわるいと言っていいと思います。わたしはそういうときは、クエリに関してはSQL直接書くレイヤーである「ReadLayer」っての作っちゃって、そこでSQL組み立ててそのViewに特化したDTOにレコードぶっ込んでPresentation層に渡しちゃったりしてますね。