最近考えてるゲ開発における設計の話

最近社内の勉強会っぽいところで、ソフトウェアの設計について少し語ったのですが、何だかんだ消化しきれないところがあったのでまとめてみます。

Clean Architectureへの誤解

ゲーム開発 に所謂なアプリケーション設計パターンをおいそれと適用するのは難しい - @hadashiA

制御フローやレイヤの分け方については特に何も主張しておらず、「依存性ルール」だけに注目してそれを図解してみた、という部分だけが骨子で、そこに主な意味がある。DDD的な構成が全くわからない人はクリーンアーキテクチャを調べるのではなくてDDDを調べてみるべき。そもそもの全体の制御フローをどうすべきかの指針がまずあって、その上で「依存性ルール」に反しているところを「逆転」させる、これがこの理論の使い方。

原文を読みに行ったら "The Dependency Rule" って8回も大文字で書かれてた。ボブおじさんすまん…。

普段触ってるコードを眺めてみるとUseCase層は割とData層やPresenter層に興味があって、この「依存性ルール」を逆転させることで、切り分けて考えることが出来るなと思う。切り分けることで、テストでスタブ刺すのが楽になったり、フレームワークという詳細の選択の自由を残すことが出来る。

そう、Unityは詳細。…んな、そんな綺麗に切り分けれるかというとやっぱコスパ悪いので無理なんですよね。結局Unityに依存したコードが大半なので。

ただゲ開発でも、部分的にClean Architectureを適用して切り分けられる箇所(決済とか)も有るよねってことを先輩と話したりしてた。

ドメイン駆動設計(DDD)

大筋ではそんな難しいことは言ってなくて、割と当たり前のことを説いてると思う。問題解決するためにコード書いてるんだから色々悩んだ開発者がDDDに至るのはごく自然な現象なのかと。

ドメイン駆動設計とは何なのか? ユーザーの業務知識をコードで表現する開発手法について|CodeZine(コードジン)

 ドメインの概念や事象を理解し、その中から問題解決に役立つものを抽出して得られた知識をソフトウェアに反映する。こういったことはソフトウェアを開発する上で当たり前の行為です。しかし同時に技術指向の開発者であればあるほど疎かにしやすい工程でもあります。

 たとえば最新のフレームワークや開発手法、最新技術といったワードは開発者の心を躍らせるものです。本来であれば問題を解決するにはその問題と向き合うことが求められますが、技術指向の開発者は技術的なアプローチで解決を図ろうとしてしまいがちです。結果としてできあがったものが的外れなソフトウェアでは目もあてられません。ピカピカのハンマーは開発者の目を曇らせ、見るものすべてを釘に変えてしまうのです。

 こういった悲惨な結果を招かないためにも、ソフトウェアを適用する領域(ドメイン)と向き合い、そこに渦巻く知識に焦点をあてる必要があります。よく観察し、よく表現すること。ソフトウェアを構築する上で当たり前の行為です。しかし当たり前のことを実践することこそが難しいのです。ドメイン駆動設計のプラクティスはその実践を補佐するでしょう。ドメイン駆動設計はいわば当たり前を当たり前に実践するための開発手法なのです。

結果、出来上がったコードを読むだけでドキュメント代わりになるような。極端に言うと企画の人がコードレビュー出来るぐらいまで落とし込むイメージ。

で、それを実現するためのモデリング手法とか、Serviceとか、選択するアーキテクチャとか色々細かいプラクティスはあるけどそんなものはやっていく中で覚えれば良い。というか僕も細かいところはまだよくわかってない。そこが難しくて意味が有ると思うけど…、いきなりHowから入ると詰む。

そうやって苦労して作り上げた、コアなロジックだけを叫んでるような抽象度の高いコードって必然的に上位レイヤーに置かれるので、全体の見通しが良くなる。ドメイン層だけ読めば、何となくソフトウェア全体が見通せる。

プログラムの依存関係とモジュール構成のこと #オブジェクト指向 - Qiita

単方向に依存関係が成り立っているレイヤー化が進めば自然と上位レイヤーがハイコンテキストになり、汎用的なものは下位レイヤーに集まる傾向になると思います。
そしてある時点より下位のレイヤーは技術的な関心事が中心になり、上位のレイヤーは要件を表すようになってくるでしょう。

DI(IoC Container)

Unity専用最速DIコンテナVContainer と、UnityにおけるDIの勘所 - hadashiA

制御フローを単純にする補助の為にDIを使う。

ゲ開発の泥臭さ

抽象度の高いモデルを形作ってソフトウェア全体の振る舞いはそれに従うだけとなれば、保守性が高まってみんなハッピーで良かったのだけど、ここは修羅のゲ開発現場だった。

ゲーム開発 に所謂なアプリケーション設計パターンをおいそれと適用するのは難しい - @hadashiA

ゲ開発で、そのゲーム固有の色々なものをつくる、ということは、imgタグそのものを実装する、という姿が近いとおもっている。つまり、人間に理解しやすい抽象化されたデータよりも、もっと細かくて複雑なフレーム毎の見た目の変化をプログラムしていくことになることが多い。

Web上で動くようなアプリケーションに比べて圧倒的にViewの関心事が多い。ただViewもViewで抽象化には取り組んでおり、後述するDSLの話につながってくる。

あと、ロジックの抽象化しやすさはゲームにもよると思う。ストラテジーなゲームは割と抽象化しやすいのでは無いだろうか。

DSL

企画の人がコードレビュー出来るぐらいまでって書いてピンと来るのが、究極の形はDSL(domain-specific language)なのでは?となる。

ゲームは非常にViewの関心事が多いと書いたのだけど、ViewはViewで抽象化に取り組んでてそのためにDSLが用意されてる領域が結構ある。シェーディング言語とか。

領域を絞って抽象化に成功することで、汎用的なプログラミング言語には出来ることの多さではかなわないけど、特定の領域では強力な武器となり得る言語を作り上げることが出来る(漫画っぽい話になってきた)。

まとめ

色々吐き出したり引用したりで、自分なりの解釈がまとまってきた。設計に明確な答えなんて無いけど、また後で見直したりして、このときこう考えてたんだなぁといった記録にしたい。

※追記 DSLに対してまだ認識不足なところがあったので少し記述削った

追記(2023/10)

この記事書いたときは、抽象レイヤーの純粋さみたいなものを追い求めてた気がする。特化したDSLを創り上げてそれでコアロジック全部記述出来れば良いみたいな。制御フローが肝心だと勘づいてるのは良くて。ただ理想論を振りかざしてるようにも見える。

もっと目の前の問題を観察することが大事なのだと思う。パターンを最初に取り出すのではなくて、まずは観察から入る。背景を時間の許す限りとことん調べてみる。パターンに振り回されて眼の前の問題と向き合えないなんてことがあっては行けない。ゲ開発は毎回同じパターンが通じる親切な環境じゃない。

普段触ってる領域外、Unityエンジニアだったら他エンジンだったりWeb周り、ソフトウェア以外の知識に目を向けるのがヒントかもしれない。アナロジー。知識の幅がもっと欲しいなと思う。