MVC云々の話・コードの量の問題じゃないかな

Inspired by みねこあさんとこ

発端:
Life is beautiful: Ruby on Railsの「えせMVC」の弊害

Modelの外部インターフェイスの設計においてもっとも大切なことは、この「データの整合性」の責任を100%Model側で引き受け、「Controllerが何をしてもデータの整合性だけは絶対に壊れない」ように作っておくことである。

まったく同意です。

それに対するひがさんの反応:
えせMVCについてそろそろ一言言っておくか - yvsu pron. yas

私は、Modelには収まりが悪いビジネスロジックはServiceにおくことをすすめています。この辺は好みで、永続化されないModelにおく方法もあります。Controllerにあったほうが良いこともあるでしょう。

まったく同意です。

MVCとはなんぞや」的な話には実のところ興味はないのですが、私の理解では、Modelはデータのモデルそのものであり、自分自身の整合性に絶対の責任を持たなければなりません。銀行口座で言うなら、私が自分の口座から1000円引き出したら、私の口座の残高が1000円減り、通帳に記入するための履歴にも何月何日に千円引き出した、という記録が残らなければなりません。あるいは、私が誰かの口座に1000円振り込むなら、誰かの口座の残高が1000円増えて、その誰かの、通帳に記入するための履歴にも何月何日に千円振り込まれたという記録が残らなければなりません。

では、私がトチ狂ってカラーのはてなスターかなにかを買おうと思ったとして、自分の口座からはてなの口座に振り込もうと思ったとき、これを

  1. 私の口座から1000円引き出す
  2. はてなの口座に1000円振り込む

というふたつの処理を続けて行うことだ、と抽象化することは、無理のある話ではないでしょう。ていうかこれに無理があるというのならそんな「ソフトウェア工学」はそもそもなにかがおかしい。私自身がATMの前に立って、1000円引き出して紙幣を手にして、それをはてなに現金で振り込むのと実質的には同じことをするわけで、単に省力化のための仕掛けなんですから*1

だから、この処理は、Modelの、「誰かの口座から一定金額を引き出すメソッド」と「誰かの口座に一定金額振り込むメソッド」を順に呼ぶことで実現できなければならない。

もちろん、私の口座から1000円引き出すだけ引き出して、エラーか何かで、はてなの口座に1000円振り込まれることなく終わってしまっては困ります。こういうトランザクションの保証は、Modelにおける「データの整合性」よりも上位で保証しなければなりません。

「私の口座から1000円引き出す」という操作は、それ自体、Modelにとって整合性の取れた操作です。だからこれは単発で実行できなければおかしい。そして、私がはてなに口座から1000円振り込もうと思ったら、「私の口座から1000円引き出す」「はてなの口座に1000円振り込む」というふたつの操作を続けて行う、と考えることもおかしくない。でも、このふたつの操作を前半だけやってしくじったら、「Modelの整合性」は壊れないはずですが*2、ユーザである私は非常に困る。つまり、Modelにとっての整合性は崩れていないのかもしれないですが、ユーザにとっての整合性は崩れているわけです。

しかし、Modelが「誰かの口座から一定金額を引き出すメソッド」をControllerに公開しているのなら、

Life is beautiful: Ruby on Railsの「えせMVC」の弊害

Modelの外部インターフェイスの設計においてもっとも大切なことは、この「データの整合性」の責任を100%Model側で引き受け、「Controllerが何をしてもデータの整合性だけは絶対に壊れない」ように作っておくことである。

Controllerが何をしても「ユーザにとっての整合性」を崩さないようにすることは不可能です。

で、現実的なアプリケーションにおいて――というのはいわゆるWebアプリとか業務アプリとかの世界ですけど――「誰かの口座から誰かの口座にお金を振り込む」みたいな操作は、ひとつの「ビジネスロジック」として切り出せる操作だと思うんですよ。ていうかこれがビジネスロジックでないんならビジネスロジックって何なんだ。

そして、この「ビジネスロジック」は、ユーザの要望に応じて、ぶくぶくと増殖していく運命にあります。、「Controllerが何をしてもデータの整合性だけは絶対に壊れない」ことを実現したいなら、Modelは珠玉のように小さなコードで実現されていなければなりません。しょっちゅういろんな人がいじくりまわし、ぶくぶくと増殖していくコードについて、整合性を保つことは困難だからです。

Modelの整合性はModelが絶対的に責任を持つけれど、ユーザにとっての整合性は、もう1階層上の階層で責任を持たなければならない。それがCotrollerなのかServiceと呼ぶのかはわからないけれど。

ワープロやドローツールなら、Modelの整合性と、ユーザにとっての整合性の差異は大きくありません。SmalltalkMVCMicrosoftのDocument-Viewはこのへんの構造を前提としているように私には見えます。でも、アプリによっては、「Modelにとってのatomicな操作」と「ユーザにとってのatomicな操作」は食い違うんじゃないの? と私は思うわけです。

*1:手数料云々の話はひとまず無視。

*2:「私が自分の口座から1000円現金で引き出す」という操作はそれ自体単独で成立するから。もっとも、口座から振り込む場合、私の手元に1000円札が来ることはありません。でも、まさか「私の手元」(ATMからお金を吐き出すところ)までをプログラミングにおけるModelに組み込むのは無理なんじゃないでしょうか。