(駄文) MVCも階層化アーキテクチャもCleanアーキテクチャもどうでもいい。外形テストと継続的なリファクタリングをしろ。

アーキテクチャは正直なところなんでもいい。MVCで作ろうがDDDで作ろうがOnionだろうがCleanだろうが押し並べて、使えない。移ろいやすくて2年ごとに変わって、そのくせ完全に理解しないと使えないと喚いた上で、十全に理解した上で使うとまるで銀の弾丸であるように喧伝される。繰り返す、使えない。

それはまさにジャーゴンの代表みたいなものだ。ソフトウェアアーキテクチャの提唱については結局のところ次の1枚に集約される

f:id:den8:20180816020213p:plain

僕はアーキテクチャが無駄だといっているわけではない。MVCもMVVMもDDDによるLayerd architectureもOnionもCleanも無秩序なコードに一定のルールを課して、プログラミングの指針を示そうとしている。

でも、そんなもの結局のところ2年毎にシステムの開発担当者が変わるような職場で完全に理解を共有する、共有し続けることは不可能だ。DDDのディの字も知らない人間がコードライティングに参加するかもしれないし、後任のリーダーが突然今日からDuty Architectureにすると言い出すかもしれない。

だから僕は、内部アーキテクチャよりも外部とのインターフェイス、マイクロサービス的な視点で外部に露出したRESTやAMQPなどの接点を偏重と言っていいまでに重要視する。そこは内部アーキテクチャと違いそうそう変えられないからだ。

一方インターフェイスが同じであれば内部アーキテクチャなんてものは結局のところいつでも変えられる。

だからこそ、僕は単体テストを全く重要視しない。単体テストは内部アーキテクチャ、もっと言えばクラス構成と一蓮托生だからである。それはプログラマーにもったいない精神を生み構造的リファクタリングを意識的・無意識的に阻害する。

だからこそ、僕は統合テスト・外形テストと呼ばれるシステムインターフェイスレベルのテストを偏重と言っていいまでに重要視する。そこはめったに変わるものではないためだ。またそれはシステムの仕様になる。それはシステムの利用者との契約であり、ドキュメントである。

そして、(実はこっちがもっともいいたかったことだが)アーキテクチャなんかより継続的にリファクタリング・テスト・リリースできる開発環境の構築こそが内部構造なんかより10倍ぐらい重要なんだ。

コード書き始めて15年、業務アプリケーション開発に携わって6年だが、この6年の間にそのときのリードコーダーのきまぐれで選ばれたアーキテクチャで作られた、たくさんのひどい業務アプリケーションに携わった。少なくとも片手では数えられない程度のシステムは見てきている。

その全てが最初は理想的で、革新的で、この世のすべてのソフトウェア開発問題を解決する素晴らしいアーキテクチャを使って書き始められた。 しかし、現実はどうだろう。 リードコーダーは転職し、後任のコーダーは新卒1,2年で、当初の理想は見る陰もない。そこには仕様すら定かでない業務アプリケーションが転がるだけだ。

認めろ。内部アーキテクチャなんぞに俺のクソほどの価値もない。 それは理解されず、徹底されず、6ヶ月ごとに入れ替わり2年で移籍する開発者へ教育する価値もなければ時間もない。業務アプリケーションのコーディングへ携わる人間すべてへ同じアーキテクチャへの理解を強制することはできないし、現実的ではない。

そんなものより、変わりゆくシステム要求を満たしつつ成長し続けられる開発環境こそが重要なんだ。 ここでいう開発環境とはIDEのことではない。 システムインターフェイスのサービスディスカバリ、API仕様の発布、APIドキュメント、CI、CD、漸近的でPRベースの開発スタイルすべてのひっくるめである

単体テスト、つまり内部クラスのテストではなく外形テスト、システムインターフェイスのテストをしろ。 それもakka-http-testkitやその他のコントローラ専用テストツールを使うのではなく、実際にサーバを動かしてsttpやその他httpクライアントライブラリを使って実際にREST APIを叩くんだ。

ほとんどの人間がやったことがないので最初は面倒に感じるかもしれないが、それはそんなに難しくない。 もし仮にそれがそんなに難しいとしたら、お前たちはそういうひどいものをシステム利用者に提供していることになる。それはそれでいい教訓になるだろうし、次からはもう少し頭を使ってAPIをデザインするようになるだろう。

あと、前述の通り内部アーキテクチャは2年毎に変わるから、導入するにしても極力シンプルなものを導入しろ。それも広く使われている用語を使ってだ。

例えば、かなりハイレベルな職場ですら全体で確かに共有できるものはせいぜい以下ぐらいのものだろう。

  • リポジトリ: DBや外部サービスとのやり取りを集約する
  • エンティティ: リポジトリから取り出されるもの、リポジトリで保存されるもの
  • サービスやユースケースなんてものは作るな。それは物(Object)ではないし、オブジェクト指向の原則から外れる。それはロジックとデータ構造の関係を曖昧にする
  • 便利(util)クラスも同じ。経験上、implicit classやオープンクラスを活用することで継承を使うことなく機能を追加できることがほとんどだ