僕は普段、仕事(チーム)では『実践テスト駆動開発』に示されているようなモックをフル活用したアウトサイドインの開発スタイルを取っているのだが、『単体テストの考え方/使い方』を読んで以来、「モックを減らせないかな?」ということを考えている。(より正確に言うと、モックのメリットを最大限享受し続けながらも、モックのデメリットを最小化できないか? というのがテーマで、その実現に向かうための仮説の一つが「とりあえずモックを減らせないか考えてみるとよさそう」、という感じだ)
そんな折、『なっとく!関数型プログラミング』を読んでいて、「関数型コア」という考え方を知った。
ビジネス関連のロジックとして考えられるものをすべて嘘をつかない純粋関数として抽出し、それらを関数型コアとしてひとまとめにする。そして、それらの関数を個別にテストしてから、メインプロセスで使う。このようにして、関心事のもつれを少しずつ解いていく。(『なっとく!関数型プログラミング』300ページ)
この「関数型コア」は「命令型シェル」と対比される(以下のページがわかりやすい)。『なっとく!関数型プログラミング』では、これを「関数型アーキテクチャ」と呼んでいた。
先の引用にもあるとおり、関数型コアには副作用や外部への依存がないため、容易に単体テストすることができる。モックも不要だ。(もちろん、関数型コアの中でモックを活用することもできるだろう。だが、少なくとも必須ではなくなる)
ええやん?
そう考えていたある日、こないだのObject-Oriented Conferenceで聞いた田中ひさてるさんの講演が面白かったので買った『ちょうぜつソフトウェア設計入門』の、クリーンアーキテクチャを解説する第1章に以下の記述を見つけた。
もっとも内側の円には、ユーザーがどんな操作をしたいかとは無関係に、普遍的に存在する本質を入れます。(中略)
この本質から排除された、ユーザーが行いたい操作を表現するロジックを、内側からもうひとつ外の円に入れます。
なるほど。
もっとも内側がエンティティやドメインと呼ばれるもの、ひとつ外がユースケースと呼ばれるものだ。
ここで気づいたのだが、僕は「関数型コア」というアイデアを聞いたとき、無意識のうちに「(クリーンアーキテクチャでいうところの)エンティティやドメイン=関数型コア」なのでは、と思っていた。
しかし、そうではないことに気づいた。
関数型コアは「副作用や外部への依存がない」ことがその本質だ。一方で、エンティティやドメインは、(ひさてるさんの言葉で言えば)「ユーザーがどんな操作をしたいかとは無関係に、普遍的に存在する」ことがその本質なのだ。(あくまで結果的にだが)おそらく前者は、後者を「含む」関係になるだろう。
どちらのアーキテクチャも、テスタビリティをコアにものごとを考えているわけではない。テスタビリティはあくまで「重要な関心事の一つ」にすぎない。
一方で、僕はテスタビリティ、特に「いかにモックを減らすか」で頭がいっぱいだったので、両者を(一時的に)誤って同一視してしまっていたのだ。
いや……本当にそうだろうか?
ボブおじの叫びが聞こえる。
アーキテクチャのルールはどれも同じである!(『クリーンアーキテクチャ』23ページ)
関数型アーキテクチャは、ボブおじが知らなかった新種のアーキテクチャなのだろうか?
大方の予想通り、そんなことはない。
適切に構造化されたアプリケーションは、変数を変更しないコンポーネントと変更するコンポーネントに分離されている。こうした分離は、変更された変数を保護する適切な規律を使うことで実現されている。
アーキテクトならば、不変コンポーネントにできるだけ多くの処理を押し込み、可変コンポーネントからできるだけ多くの処理を追い払うべきだろう。(『クリーンアーキテクチャ』75ページ)
関数型アーキテクチャもまた、クリーンアーキテクチャの一種だったのだ。
実際、前出の「関数型コアと命令型シェル」を説明したページにもこう書いてある。(翻訳は筆者による)
命令型シェルは関数型コアを呼び出すことができるが、関数型コアは命令型シェルを呼び出すことはできないし、命令型シェルの存在を認識すらしていない。これは依存性のルール(たとえば、Clean Architectureを参照)としても知られている。
ボブおじのあのページ(例の同心円が載ってるページ)参照されとるやん。
なんの話だっけ。
別に何と何が同一とか、何が定義として正しく、何が間違っているみたいな議論には(それほど)興味はない。
ただ、以下の3つのことを(改めて)確認できたのかなと思っている。
クリーンアーキテクチャは、特定のレイヤー構造を指し示すものではなく、依存性のルール(下位の仕組みは上位の方針にのみ依存する)がその本質である
関数型アーキテクチャは、できるだけ多くのコードを副作用のない「関数型コア」で構成することにその主眼がある。依存の方向を「外部とのやり取りを行う命令型シェル」から関数型コアへの一方向に限定しているという点で、クリーンアーキテクチャの特徴も兼ね備えている
「アーキテクチャのルールはどれも同じである!」
まあ、最後の一つには異論のある人もいるだろうが(僕も別に、永遠に有効な普遍的真理だとは思っていない)、かなり強力で汎用性の高い考え方ではあるんだよな、ということを改めて思った。
そして、「モックを減らせないか」という課題設定自体は必ずしも悪くないと思うが、他の色々なものごととバランスを取りながら考えていかないとうっかり踏み外しそうだな、ということも思ったのだった。
アーキテクチャのルールはどれも同じかもしれないけど、ルールをどう使うかが面白いところだよね。