アーキテクチャの目的はコストの最小化

とりすーぷ氏:「Unityにおける設計パターン」というタイトルで発表します。よろしくお願いします。

私は、とりすーぷという名前でいろいろと活動をしています。Microsoft MVPを2018年から継続で受賞しています。ありがとうございます。最近『UniRx/UniTask完全理解』という本を出したので、もしよければ手に取ってみてください。

今回は、設計とアーキテクチャに触れたいと思っています。10分の発表に対して、調子に乗って100枚を超えるスライドを作っちゃって、絶対に10分では終わらないので、発表はだいぶ端折ってかなり早口でいきます。公開する資料はフル版なので、そっちを見てください。発表はだいぶすっ飛ばしていきます。

今回の内容なんですが、正直「この本を読んでください」という話です。この本を薄めて、まとめて話します。

本題に入っていきます。「設計」と「アーキテクチャ」とは何なのか。「設計」は、クラス設計やコンポーネントの構成や全体の構成などのことで、何をどう作っていくのかという指針ですね。これを俯瞰した設計を「アーキテクチャ」と呼びます。

家の建築で例えると、壁の位置や形状や配管、配線などの細かい部分を設計と呼び、家全体のかたちや間取りや部屋のレイアウトなど、俯瞰的に見た設計のことをアーキテクチャと呼びます。この2つは、地続きであると覚えてください。詳細な設計の連続がアーキテクチャを構築します。なのでこれは不可分ですね。

なぜこれを使うかというと、システムの開発、保守、運用にかかるコストを最小限にするためです。どういうことかというと、クソコードに振り回される時間を減らしたり、後の仕様変更に柔軟に対応できるようにしたりするのが目的です。

まとめると、コストを最小限にするというのがアーキテクチャの目的です。これを死守するのが開発者の使命なので、安易に「設計を捨てる」という選択肢を取らないようにしましょう。

設計レベルを5段階で定義

Unityにおける設計レベルを今回定義したので、話していきたいと思います。自分が勝手にレベル分けをしました。勝手に決めたので異論、反論は受け付けます。むしろ議論したいなと思っています。図があるんですが、青い箱がGameObject、MonoBehaviourだと思ってください。緑がパッケージで、C#と書いてあるのがピュアなC#のファイルです。

まず、設計レベル0です。設計が何も存在せず、好き勝手に作った状態ですね。GameObject同士がグチャグチャに参照し合っていて、神クラスがベタベタとある状態です。

ここからレベルが1個上がると、制御フローが整理された状態になると思っています。オブジェクト同士の役割が明確になって、参照関係が整備されて、機能ごとに整理されるという状態になっています。ただ、密結合なのはそのままです。

さらにレベルが上がると、疎結合化が進みます。制御フローと依存関係の分離と書いているんですが、SOLID原則が意識され始めたぐらいだと思っています。さらにレベルが上がると、モジュール化が意識されます。モジュール同士の安定性を考えたり、機能の再利用性を考えたり、Assembly Definitionファイルが切られたりをし始めたぐらいかなと思います。

レベル4になるとMonoBehaviourを使わない実装がどんどん増えてきます。レベル5になると、アーキテクチャという概念が意識され始めて、全体を見てキレイな設計をしようという意識が芽生えます。

というわけで、レベルを分けるとこんな感じになるかなと思います。0から5で6段階に分けてみました。レベルを上げるメリットなんですが、上げれば上げるほど長期開発においてコストが下がります。仕様変更に柔軟に対応できるようになったり、大人数で開発しても破綻しにくくなったりします。

チームが難しいと感じたら設計のレベルを下げてもいい

ただ、レベルを上げるデメリットもあって、開発者の負担も上がります。規模に見合わない設計レベルを採用すると逆に手間が増えたり、レベルを維持するために、開発者に求められる設計スキルが高くなったりします。ほかにも、動作パフォーマンスが落ちる場合もあります。チューニングのためにベタ書きをしないといけないので、Unityにべったりなコードを書かないといけません。

なのでこの辺はトレードオフだと思っています。理想は長期的な運用なんですが、現実はさまざまな理由で妥協しなくてはなりません。

大事なことは、「チームがやりやすいレベルで止めてもいい」ということです。難しいと感じたらレベルを下げてもいいです。ただ、最低限レベル1は維持したほうがいいと思っています。設計レベルは1から5のどれかにしないといけないというわけではなくて、ハイブリッドに書いてもいいです。

Unityにべったりな部分と、レベル5でアーキテクチャを意識してキレイに書いた部分を1つのプロジェクトで扱ってもいいという話になります。まとめると、どのレベルで開発するかはバランスを見てくださいという話です。

設計レベルを上げるために制御フローを意識する

この設計レベルを上げていくためにはどうするか。今設計レベル0をやっている人が、この壁を越えてどんどんレベルを上げていくためにはどうしたらいいかなんですが、設計をまったくしていないという人は、制御フローの整理を意識するといいと思います。フローさえ整っていれば、スパゲッティコードは最低限回避できます。

制御フローとは何かというと、誰が誰に命令をするのかという流れです。オブジェクトの責務やメッセージの流れを考えて、グチャグチャになっているのを整えることです。やり方は、オブジェクトの持つ役割をハッキリさせた上で、さらに主従関係も決めてデータの流れをわかりやすくするというものです。これができていれば、レベル1に上がると思います。

レベル1から2、またはそれ以上を目指すならどうするかですが、この制御フローに実装が振り回されない対策を考える必要があります。制御フローのコードをキレイに書くと、密結合になって柔軟性がなくなっていくので、どこかで分離しなければいけなくなります。そのときに学ぶべきものは、設計原則ですね。設計原則は、まさにこの制御フローを変えずにどうキレイに作るかというやり方をまとめたものです。

SOLID原則やコンポーネントの原則などいろいろとあります。このあたりはこの本に全部書いてあるので読んでみてください。

図にするとこうかな? 0から1に上げるときは、制御フローを意識してください。1から2に上げるときは、SOLID原則を学んで、3に上げるときはコンポーネントの原則を学んで意識するといいと思います。

設計レベルを上げるまとめです。まず制御フローをしっかりと意識してください。意識ができたら今度はそれに振り回されないような対策をしていく必要が出てきます。

設計原則を知らないとクリーンアーキテクチャは作れない

クリーンアーキテクチャの話をしましょう。すごく雑に言うと、クリーンアーキテクチャは「君だけの最強のアーキテクチャをつくろう!」ということなんですよね。そのための指標にクリーンアーキテクチャを使おうという話で、最適なアーキテクチャは自分たちで模索して育てるしかないんですよ。

万能なアーキテクチャなんて存在しなくて、自分たちのプロダクトのニーズに合わせて、自分たちでアーキテクチャを作って考えないといけないんですよね。なのでアーキテクチャはいろいろなアーキテクチャに共通するルールをまとめたもので、こういうルールで作っていくといいよというのをまとめたのがクリーンアーキテクチャなんです。

クリーンアーキテクチャのルールはこれですね。「依存関係は上位レベルに向けて一方通行にしろ」や、「制御フローと依存関係を分離しろ」という話。よく誤解されるんですが、クリーンアーキテクチャは具体的に「こうしろ」とは言っていないんですよ。レイヤー数やコンポーネント数は変えてもいいし、構成もあとから変えていいです。最初は小さく始めて、だんだんアーキテクチャを大きくしていく。

あとからレイヤーの数を増やしていくのもぜんぜんいいよと言っているんですよ。なので、この図に振り回されちゃいけないんですよ。この図はただの例えです。「Webでもし作るなら、こういうやり方があるよね」ぐらいのノリで、別にこの通りに作れとは一言も言っていません。レイヤー数が2個でもいいという話です。

クリーンアーキテクチャについてまとめます。クリーンアーキテクチャはただの指標です。クリーンアーキテクチャをやるためには、設計原則を学ぶのが一番大事で、これを押さえておかないとアーキテクチャは作れないです。というのも、クリーンアーキテクチャ本で「クリーンアーキテクチャ」のワードが出てくるのは22章なんです。その前の21章まではずっと原則について話しているんですよ。「設計とは何か」「設計原則とは何か」って。

それだけの前提知識がないと、こういうアーキテクチャは作れないよというのを覚えておいてください。

ハイブリッドにすればUnityでクリーンアーキテクチャも可能

最後にUnityでクリーンアーキテクチャをする話です。「Unityでクリーンアーキテクチャってできるの?」とよく聞かれるんですが、基本的な考え方に則れば、共存はできます。Unityという土台の上に、クリーンアーキテクチャを作ろうと考えている人がたまにいるんですが、これは失敗します。

というのもアーキテクチャには「フレームワークと結婚するな」というセリフがあって、フレームワークをアーキテクチャの上位レイヤーに持ってくるな。さっきの円の中心にフレームワークを持ってくるなという考え方があるんですよ。フレームワークの都合でアーキテクチャが振り回されちゃいけないんですよね。

クリーンアーキテクチャの世界を作って、そこにUnityが依存するかたちにするべきで、これなら共存ができます。実際にどうするのかというと、さっき言ったレベル間の違う領域のハイブリッドをやればいいですね。

つまりこれはさっきと同じ図です。クリーンアーキテクチャで作った世界があって、Unityエンジンにべったりな世界があって、それが依存してUnity側からクリーンアーキテクチャの世界を呼ぶというかたちにします。こうするとクリーンアーキテクチャはキレイに回るかなと思います。両極端に振れずに、ハイブリッドにする考えがあることを忘れてはいけないよという話です。

Unityでクリーンアーキテクチャのまとめです。クリーンアーキテクチャはUnityでも利用できます。ただ、利用できる部分とできない部分があるので、そのあたりは切り離して作るといいと思います。補足ですが、Clean Architecture for Unityというものがあるんですが、これをそっくりそのまま利用するのはちょっとおすすめしません。

黎明期に提案されたUnity向けのクリーンアーキテクチャのテンプレートなんですが、作者本人も黒歴史だみたいなことを言っていました。ここまでやるとちょっとやり過ぎなんですよ。なので、Clean Architecture for Unityを参考にするのはいいんですが、これをそっくりそのまま使うのはおすすめしません。

バランスをとってやりやすいかたちで設計をする

最後にまとめます。最初から完璧なアーキテクチャが存在するとは思わないでください。アーキテクチャは開発過程でどんどん成長していくもので、自分たちで育てていかないといけないです。最初から答えはないです。プロジェクトの開発、保守、運用にかかるコストを最低限にするのが目的なので、それが達成できているなら、多少汚い部分があってもぜんぜんOKです。

クリーンアーキテクチャになっていなくても、設計原則に違反する部分があっても、Unityと密結合している部分があっても、それがやりやすいならそれがいいよ、というのが結論です。ただこれは、設計しなくていいと言っているわけじゃないんですよ。設計しないほうが楽だから設計を捨てちゃおうというのはダメで、バランス感をもって設計していこうという話です。

今回の参考資料はこちらです。とても良い資料なのでみなさん目を通してもらえるといいかなと思います。発表は以上です。ありがとうございました。