mvc
設計
アーキテクチャ

個人的にアプリケーションを作るときに意識しているアーキテクチャ

最初に

個人的にアプリケーションつくるときに意識しているアーキテクチャがあります。
MVCやらfluxやらの概念は基本的に苦手で、普段やっていて得られた知見から考えたもののほうが肌に合うなぁと感じます(自分で考えたので当然ですが)。

この記事ではその説明と、それをMVCに当てはめて考えたときどうなるかを説明したいと思います。
ただ、個人で考えているものなので、穴だらけの理屈だろうし、話半分ぐらいのつもりで読んでください。
Webアプリケーションの開発者なのでそれに当てはめてますが、ほかにも流用できる概念なんじゃないかと思っています。

概要

個人的にアプリケーションを作るときは以下の4つを意識しています。

  1. Entrypoint
  2. Controller
  3. Logic
  4. Environment

なのでECLEですね。
上記の単語は適当につけただけでどうでもいいものですが、それぞれにはちゃんと意味があります。

概念図

ecle.png

詳細

1. Entrypoint

これは、アプリケーションの所有者が、アプリケーションを起動する部分です。
node.jsであれば、node app.jsとか打つときのapp.jsの部分です。
Webアプリケーションであれば、ここでDBのコネクションなんかを用意して、controllerに処理を待機させると思います。

2. Controller

これはMVCのControllerと同じで、LogicやEnvironmentを用意して、それぞれに処理をさせ、
それらからあがってきたデータを、次のLogicやEnvironmentに渡すという役割です。

このControllerが持つ最も重要な役割は、アプリケーションの状態を管理することです。
ここで管理されている状態を用いて、DBアクセス、ファイルアクセス、セッション取得などを行うわけです。
※「状態」という言葉の意味は結構奥が深いのですが、ここでは説明は割愛します。

3. Logic

これは、関数型でいうところの参照透過性のある関数群です。
ここにビジネス価値のあるロジックを記載します。
ただ、プログラムの外部にでるようなIOのあるコードは、ここには書きません。

このようにLogicをIOと切り離すことには意味があって、テストをしやすくするためです。
IOがないので、とても簡単にテストが書けます。

4. Environment

Logicで扱わなかったプログラムの外に出るIOのある処理です。
DBアクセスや、ファイルへのアクセスなんかがこれにあたります。
多くの言語で、例外処理が必要になる部分です。

補足

時間のかかる処理だと、LogicとEnvironmentを分けられない場合があります。
数万件のデータを加工してまとめる処理を書くときなんか、切り分けづらいですよね。

ただ、高階関数で関数を渡すことができます。Javaであればラムダ式、Rubyならブロックですね。
なのでこの仕組みを利用して、LogicをEnvironmentに渡すことで切り分けています。
この渡す処理を記述するのはControllerです。

MVCにあてはめて

MVCに当てはめてとしていますので、ここまででModelとViewが出てこないのは、片手落ちです。
ただ、ここまで読んでこられた方はお気づきかもしれませんが、相対する概念ではありません。
単にModelやViewという捉え方をしていないだけです。

Model

Modelというのは、ECLEすべての部分で、別個に扱うべきデータ定義ではないかなと思っています。
あるいはドメインというのでしょうか。

例えばViewが扱うデータ定義が、DBのスキーマと一致しないケースなんてのはザラにあるかと思います。
それどころか、Logicとも一致しないときもあったりして、結構混乱してしまいます。
そもそも関数という視点から考えると、それぞれの関数の引数と戻り値って、それぞれデータ定義違いますよね。
だから、それぞれにデータ定義というかドメインがあるのだと思います。

つまり僕は、Modelという概念はあれどECLEで共通したModelではなく、
それぞれにModelが存在するという理解をしています。
だだ、それだとDBに新しいテーブルを定義するとModelが倍々に増えていくので大変です。
MVCはそれらを簡略して、アプリケーションでは一貫して、単一のModelを扱いましょうよ。
そのほうが楽ですよという概念なのだと理解しています。

View

この理屈でいうと、ViewというのはControllerの戻り値なので、ControllerのModelということになります。
ただ、GUIのアプリケーションにおいては、Viewがふるまいを持ったり、ユーザ入力を受けつけたりするので、
Environmentであるとも感じています。
GUIはSPA開発のときに意識しますが、いかんせん経験が少ないので、あまり偉そうなことは言えません。

なぜMVCではないのか

Modelの説明部分で記載していることですが、Modelというものの範囲が拡大しすぎている感覚がありました。
Logicを持っていたり、ActiveRecordなんかだとEnvironmentを持っていますよね。
僕の中で、それらがごっちゃになってきて混乱を招いていました。
なので分解する必要があったのです。

別にMVCを捨てているわけではない

しかしながらMVCを捨てているわけではなく、Controller、Logic、Environmentには、
それぞれ別個のデータ定義、属しているドメインがあるはずです。
それらは完全には切り分けられないかもしれないけれど、プログラマが理解しやすい形で分解することはできます。

なにもLogicだから、UserドメインのロジックとProductドメインのロジックを同じモジュールに入れることはしません。
ただまず、Controller、Logic、Environmentに切り分けてから、理解しやすい形で分割していきます。
ECLEに分けたあとにMVCを意識して、分割していく作業をするだけです。

一歩踏み込んで

仕事の内容や、メンバの考え方次第な部分もありますが、あまりLogic以外の単体テストをしたくないなと思っています。
EnvironmentやControllerの単体テストにはモックやスタブを用いる必要があり、テストしづらい上に、
ビジネスの核心であるLogicはきちんと分離されているからです。
LogicとEnvironmentを正しく組み合わせていることを確認するControllerのテストや、
決まりきったIOのコードを記載し、実行環境外の状態を想定しないといけないEnvironmentのテストはコストがかかります。
つまり費用対効果が薄いと感じます。

それらを単体テストで動きを確認するよりもアプリケーション全体でテスト(結合テスト?E2Eテスト?)するほうが、シンプルで一石二鳥だと感じるからです。
ただ、こんなこというとどこかからマサカリ飛んできそうですね。
なので、この部分はあまりこだわり過ぎずに仕事しています。

注意点

この話はMVC含め、既存の概念を批判するのではありません。
MVCというのは、非常によく考えられた概念だし、よくできている。
けれども、それがすべてではないよねってことです。
別の視点から見ることで、もっとアプリケーション開発における設計をとらえやすくしようよってことです。

まとめ

こういうアーキテクチャの話は基本的に、偉い方々が考えることだと思うので、
市井のポンコツプログラマが何を言っているんだという話だと思っていますが、
まぁ何かの参考になればというのと、アウトプットしておくのも大切かなぁということで書いてみました。

皆さんはどうお考えでしょうか。
なにか思うところあれば、コメントいただければ幸いです。