Save 37% off PRO during our Black Friday Sale! »

大規模Unityゲーム開発の設計事例 〜ドメイン駆動設計とDIコンテナを導入した一年を振り返る〜 / cedec2021-ddd

大規模Unityゲーム開発の設計事例 〜ドメイン駆動設計とDIコンテナを導入した一年を振り返る〜 / cedec2021-ddd

CEDEC2021 「大規模Unityゲーム開発の設計事例」の資料です。
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
セッションの内容
現在開発中タイトルにおける、プログラム設計に関しての知識・経験を共有します。

ソーシャルゲーム開発では、作品を素早く完成させてリリースする事も大切ですが、同時にその後の継続的な運用開発についても考える必要があります。その際に重要になってくるのが設計です。

属人化を避けたり、メンテナンス性、制作スピードを維持するためにはメンバー間で統一された設計思想が必要だと考えました。

そのために私達は「ドメイン駆動設計(DDD)」と「DIコンテナ」という2つの手法を導入しました。

導入から一年が経ち、それらを振り返りつつ事例を紹介します。

https://cedec.cesa.or.jp/2021/session/detail/s60644099db9d6

Ec2fcdc4ea7905b289967a2c4c43e154?s=128

CyberAgent SGE Engineer

November 17, 2021
Tweet

Transcript

  1. 大規模Unityゲーム開発の設計事例 ~ドメイン駆動設計とDIコンテナを導入した一年を振り返る~ Applibot 畑山政道

  2. 2 自己紹介 【経歴】 広告系Flashコンテンツ開発 → 2011年 アメーバピグ Flash開発で(株)サイバーエージェント入社 → スマホブ

    ラウザゲーム開発 → スマホUnityゲーム開発 → (株)アプリボット所属 【主な仕事】 Unity全般・設計・3D描画関係・昔はFlashでアニメーション作ったり演出・UXも考えたりしていました 畑山 政道 Unityエンジニア 2011年 中途入社
  3. はじめに・ドメイン駆動開発を導入した経緯 1 2 3 4 目次 ドメイン駆動設計とUnity適用までの具体的な解説 DIコンテナとUnity適用までの具体的な解説 その他の設計手法の紹介 5

    実装の詳細 6 まとめ
  4. 1. はじめに・ドメイン駆動開発を導入した経緯

  5. 5 スマートフォン向けのソーシャルゲームを開発中 2つの大事な要素 1. 素早く開発してアプリをリリースする 2. そのあとの運用・新機能追加をスムーズに行う つまり、開発スピードとメンテンス性の両立が必要 アプリ開発に求められる事

  6. 6 設計が重要 設計の綺麗さは金銭的な価値も生む • イテレーションが素早く回せる事で試行錯誤の回数が増え、品質が上がる • リリース後、運用に入っても機能追加がすぐに実装できる • バグが出にくい •

    バグが出ても即座に対応できる といった事はビジネス上では重要
  7. 7 設計が重要 逆に設計を疎かにすると・・ 機能実装に時間がかかり、バグが多い場合、ユーザー離れ・機会損失に繋がる

  8. 8 多人数での開発だと • 自己流の設計が至るところに散らばってしまう • 自己流がぶつかり合うと、コードレビューが難しくなる • 諦め (わからない/面倒だから良いや) •

    対立 (自己流 vs 自己流) 設計が重要
  9. 9 チーム独自の方法だと • 本を参照したり・ネットで検索出来ない • チームの外に出た時に通用しない・覚え直し チーム内で思想を統一したい・実績のある体系化された手法が欲しい 設計が重要

  10. 10 EasyとSimpleの違い https://twitter.com/t_wada/status/1377147203077111814 和田 卓人さんのtweetから引用 設計が重要 - easyとsimple

  11. 11 大規模な開発にはsimpleが向いていると判断。 Easyはモック開発であったり、小規模な開発で は向いている。 EasyではなくSimpleな方針を取る。 その代わり手数は増える(コード量は増える)が 分かりやすい構造を目指す。 Simpleの実現にDDDが向いていると判断 EasyとSimpleの違い 設計が重要

    - easyとsimple https://twitter.com/t_wada/status/1377147203077111814 和田 卓人さんのtweetから引用
  12. 12 ドメイン駆動設計とは 2003年、エリック・エヴァンスの書籍が発売 される。 Domain Driven Designの頭文字を取って DDDとも呼ばれる。 上手くシステム開発を進めるためのパターン・ ランゲージ。

    良くある問題と、それに対するベストプラク ティスについて集めたパターン集。
  13. 13 ゲーム開発ではあまり聞かない ゲームに適用できるのか? 可能。ジャンルはあまり関係ない。 ドメインに焦点をあてて開発する、というだけ ドメイン駆動設計とは

  14. 14 ドメインって何だ? 「ソフトウェアが解決しようとしている問題の対象領域」 会計システムであれば、会計に必要な金銭・帳票といった概念。 物流システムなら、物流の倉庫・貨物・輸送手段といった概念。 一意に決まるものではなく、ソフトウェアによって違う。 ゲーム制作で言うなら、そのゲーム独自の概念・ルール・仕様 ドメイン駆動設計とは

  15. 15 ゲーム開発において、わりと当たり前の事を言っている • ディレクター・プランナーとよく対話し、ゲームが必要としている独自の機 能(ドメイン)をよく理解する • 対話して明らかになった知識(ドメイン)を元に、共通のモデルを作りあげる • モデルをプログラムに純度高く反映させる どうやったらこれらを実現できるのか?

    というベストプラクティスが説明さ れている。 ドメイン駆動設計とは
  16. 16 DDDスコアカード https://www.informit.com/articles/article.aspx?p=1944876&seqNum=2 より引用 ドメイン駆動設計とは 向かないジャンル 「完全にデータ中心である」 「小規模・シンプルな場合」

  17. 17 反対に向いているのは、継続的に機能が変更され、複 雑性をもったアプリケーション。ゲーム開発はこれに 当てはまる。 向いているジャンル ドメイン駆動設計とは

  18. 18 ドメイン駆動設計 == ドメインに焦点をあてた開発 ドメインモデルをコードに正確に落とし込む事が大事。 ベースとなる技術 オブジェクト指向 ドメイン駆動設計とは

  19. 19 ここで言うオブジェクト指向とは? ・データと関数を個別に扱わずに、双方を一体化したオブジェクトを基礎要素にする ・オブジェクト間の相互作用を重視して、プログラムを構築する ・現実世界の”物”や”概念”をオブジェクトに模して表現する ドメイン駆動設計 - オブジェクト指向 +

  20. 20 逆にオブジェクト指向では無いものを考える 手続き型 データと振る舞い(関数)の記述が分かれている。オブジェクトは単なるデータ 構造としてみる。 ドメイン駆動設計 - オブジェクト指向

  21. 21 オブジェクト指向を踏まえて ドメイン駆動設計に向かないジャンル 「高速化が最重要である場合」 現在Experimental版のECS(Entity Component System)はデータ指向であり、オ ブジェクト指向では書けないため、ドメイン駆動設計が適用しずらい ドメイン駆動設計 -

    オブジェクト指向
  22. 22 「高速化が最重要である場合」 ゲームのドメイン部分はDDDで開発し、 高速化が必要になる部分はECS、のような住み分けが必要になる (おそらくECS化されるような部分は グラフィック描画、物理演算、サウンド処理、AIなど?) ドメイン駆動設計 - オブジェクト指向

  23. 2. ドメイン駆動設計とUnity適用までの具体的な解説

  24. 24 ドメイン駆動設計の原典。 しかし、かなり難しい。 ドメイン駆動やるぞ! と意気込んで読み始め ると、まず挫折しがち。 参考書籍 エヴァンス本 原典: エヴァンス本

  25. 25 PDFで配布されている。 こちらもドメイン駆動設計をコンパクトに要約 したもの。日本語版もある。 ドメインモデルを作っていく過程などの例がと ても分かりく、オススメ https://www.infoq.com/jp/minibooks/domain-driven-design-quickly/ 参考書籍 DDD Quickly

  26. 26 帯や「はじめに」にあるように、エヴァンス本にあるプロ グラミングに適用するパターンをメインに紹介している。 ・チーム内で読書会を実施 ・その後も継続的に勉強会実施 ドメインについても多く言及されている。 その名の通り、入門に最適。 https://www.amazon.co.jp/dp/B082WXZVPC 参考書籍 ドメイン駆動設計入門

    ボトムアップでわかる!ドメイン駆動設計の基本
  27. 27 ドメインモデリングをはじめとして、DDD全般的に解説 されている。 ドメインモデリングについて書かれている書籍は少ないの で、大変参考になる。 https://booth.pm/ja/items/1835632 参考書籍 ドメイン駆動設計 モデリング/実装ガイド

  28. 28 PDFで配布されている。 全容を把握するのに非常に役に立つ。 https://www.domainlanguage.com/ddd/ reference/ 日本語に訳している方も https://zenn.dev/takahashim/books/fb4cdc32f8e95c 参考書籍 DDD Reference

  29. 29 改めてゲーム制作のドメインとは何か考える 「ソフトウェアが解決しようとしている問題の対象領域」 「ゲームが表現しようとしている対象領域」 ・自分たちが作っているゲームで表現しようとしているもの ・ゲームが提供する「遊び」 参考: ドメイン駆動設計 モデリング/実装ガイド -

    little-hands - BOOTH https://booth.pm/ja/items/1835632
  30. 30 改めてゲーム制作のドメインとは何か考える 「ゲームが表現しようとしている対象領域」 データベース ファイルI/O 物理エンジン UI サウンド処理 描画エンジン ユーザー入力

    世界観 キャラのスキル 成長ロジック ダメージ計算 キャラ性能 通信 行動エリア 地形 キャラ属性
  31. 31 データベース ファイルI/O 物理エンジン UI サウンド処理 描画エンジン ユーザー入力 世界観 キャラのスキル

    成長ロジック ダメージ計算 キャラ性能 通信 行動エリア 地形 システムを構成する要素から、汎用的な部分を抜いて残る部分 キャラ属性 改めてゲーム制作のドメインとは何か考える 「ゲームが表現しようとしている対象領域」
  32. 32 • 自キャラの成長ロジックなど • どうやってレベルアップする? • 武器/防具を装備する? • スキルの存在 •

    インゲーム • ゲームのルール • いつスキルが発動するのか? • ダメージの計算方法は? 改めてゲーム制作のドメインとは何か考える 「ゲームが表現しようとしている対象領域」→ ゲーム独自の概念・ルール・仕様
  33. 33 自分たちが作っているゲームで実現しようとしているもの • アクションゲームだと • ステージ情報 • 何がどこに配置される? • 壊せる・壊せない・移動する

    etc.. • 自キャラ・敵キャラの配置 • 自分・敵キャラのステータス • HP、MP、攻撃方法 etc.. • ジャンプする・敵を踏みつける事で敵を撃退する、のようなゲーム固有のルール 改めてゲーム制作のドメインとは何か考える
  34. 34 将棋ゲームのドメイン知識 の一例 • 登場キャラクター • 40枚の駒 • 駒の性能は8種類。それぞれ動ける範囲が違う •

    成長ロジック • 成駒。動ける範囲が増える • mapの広さ・位置 • 9x9の81マスで戦う • 相手の駒を取り、保持する/使う 改めてゲーム制作のドメインとは何か考える
  35. 35 • 9人 vs 9人 • 選手には打率・防御率・スタミナ、etc…などの能力パラメーターがある • ストライク3つでアウト •

    バッターはボールを打ち返す • 攻撃側・防御側に分かれ、交互に入れ替わる • これらにゲーム特有のルールも加わる • 選手の成長要素、必殺技、アイテムetc.. 野球ゲームのドメイン知識 の一例 改めてゲーム制作のドメインとは何か考える
  36. 36 これらのドメイン知識をゲーム中で扱えるように、モデルを作りあげる。 オリジナルゲームの場合、最初から明確な事は多くなかったりする。 対話・モデリングを繰り返し、ぼんやりしたモデルの輪郭をハッキリさせていく。 改めてゲーム制作のドメインとは何か考える

  37. 37 ドメインとそれ以外で分離させる事が大事。ドメインロジックをUIコンポーネントに 書いたりしない。 Viewとドメインを分離する。ここらへんはMVC的な発想と同じ。 ドメインの分離

  38. 38 どういう時に、どういう条件で点滅するのか? をViewには記述しない。 瀕死状態の定義や、瀕死状態かどうかの判断 を書くのはドメインロジック。 お題 「瀕死のキャラクターが点滅する」 ドメインの分離

  39. 代表的な概念・パターンの紹介

  40. エヴァンス本で紹介されているパターン一覧 DDD難民に捧げる Domain-Driven Designのエッセンス 第1回 ドメイン駆動設計とは https://www.ogis-ri.co.jp/otc/hiroba/technical/DDDEssence/chap1.html より引用 重要 + 実例が示しやすい赤枠部分を中心に紹介します

  41. 41 アプリが対象とする領域の専門家のこと 銀行の業務支援アプリを作るなら、 銀行の業務を熟知する銀行員がドメインエキスパート ゲームにおけるドメインエキスパートは? 現プロジェクトでは、ディレクター・プランナーといったゲームの ルール・世界・仕様を考える人たち、と捉えた。 ドメイン エキスパート

  42. 42 ドメインエキスパートと絶えず対話し、ドメインをうまくモデリングするのが大切。 仕様書をそのまま実装する、というのはNG。 仕様書に記載された機能の裏にある目的などを理解する必要がある。 ドメイン エキスパート

  43. 43 プロジェクト内で使う共通の言葉を定義する。 開発者とドメインエキスパートが同じ単語を別の意味で用いないようにする。 ユビキタス言語

  44. 44 例) キャラクターが持つ特殊能力について・・ 使用する用語をプロジェクト全体で統一する。 バラバラだと変換コストが発生するし、勘違いも生まれる。 アビリティ スキル スキル ユビキタス言語

  45. 45 プログラム中に使われる関数・変数もユビキタス言語を採用する。 しかし日本語だった場合は難しい。 そのためにも、現プロジェクトでは英語との対応表を作って管理している。 サーバー・クライアントでプログラム内で使われる単語を合わせるのにも役立つ。 ユビキタス言語

  46. プログラムに適用出来るパターンを一部紹介 値オブジェクト 1 エンティティ 2 3 リポジトリ 4 サービス 5

    集約 6 境界づけられたコンテキスト 7 レイヤードアーキテクチャ
  47. 47 • C#の値型とは無関係 • 一度生成されたら中身が変化しない。不変である。 • 中身が同じなら等価とみなす 1. 値オブジェクト

  48. 48 実装に面倒な箇所があるが、 不変性によって実装は大幅に単純化され、安全に共有や参照渡しができるようになる という大きなメリットがある DDDに関係無く有用 1. 値オブジェクト

  49. 49 値オブジェクトを 疑似コードで表現すると 1. 値オブジェクト

  50. 50 現プロジェクトでは、 全てのフィールドをreadonlyにして、コンスタントラクタで値を設定している 1. 値オブジェクト 値オブジェクトの実装について - 不変の実装

  51. 51 厳密には • 全てのフィールド変数が同一であるか比較するEquals()を用意 • 全てのフィールドを考慮したGetHashCode()関数を用意する必要がある 1. 値オブジェクト 値オブジェクトの実装について -

    等価の実装
  52. 52 Equals()、GetHashCode()があると・・ 1. 値オブジェクト 値オブジェクトの実装について - 等価の実装 中身で比較される

  53. 53 1. 値オブジェクト Dictionaryのkeyとして使う時、中身ベースで使用される これはstructのVector2の例だが、これと同じ事が出来るようになる。 値オブジェクトの実装について - 等価の実装 Equals()、GetHashCode()があると・・

  54. 54 • データベースのエンティティとは無関係 • 生成された後、中身がどんどん変化していくもの 2. エンティティ 同一性比較のため、目印となるidを持つ。idが同じであれば、中身が違っても等価 とみなす 同一性によって区別される

    中身が変化しても同一のものとして扱う
  55. 55 現プロジェクトでは • 変化していくユーザー情報 • 成長していくキャラクター情報など • 同一のキャラクターが、level、hp、mpなどのパラメーター変化していく事を表現す る 2.

    エンティティ
  56. 56 エンティティを 疑似コードで表現すると 2. エンティティ

  57. 57 「物」としてモデリングできないものもある。そうしたものは値オブジェクトやエ ンティティを取り扱う「サービス」として実装する 3. サービス 気を抜くと手続き型となる。多用しないように注意。

  58. 58 関連するオブジェクトの集まり。データを変更するための単位として扱われる 4. 集約 OrderItem Create Destroy Price VatRate Value

    to_i to_f Customer Order add_item remove_item total_price Entity Entity Entity Value object 窓口 = 集約Root 集約内部 この中はカプセル化されている Aggregate 窓口を通さないアクセスは認めない 引用 Facade Pattern as the way to implement Aggregate http://rubyblog.pro/2017/05/facade-pattern
  59. 59 ひとつの塊として扱う 塊の中にアクセスするための窓口を用意しており、窓口を飛び越して中身にアクセス 出来ない Facadeパターンによく似ている 4. 集約

  60. 60 Facadeパターン Facade適用後 Subsystem内に、 外から自由にアクセスしている 窓口を通してしかアクセスできない 引用 Facade Pattern as

    the way to implement Aggregate http://rubyblog.pro/2017/05/facade-pattern 窓口を設ける事で、複雑な構造をカプセル化する 4. 集約
  61. 61 データ(集約)の置き場所 データストアを操作する処理をカプセル化する。集約の保存・取得はここを通す。 他のクラスはデータストアを直接操作しない。 DBがある事を意識せず、メモリ内コレクションのように振る舞う。 5. リポジトリ 依頼者 集 約

    集 約 write read 保存 問い合わせ 返却 リポジトリ
  62. 62 複数チームで同時開発をしていると、 ある概念に対して複数のモデルが出来る事がある。 これに対し、無理に統一された大きなモデルを作る必要はない。 6. 境界づけられたコンテキスト メインロジックが扱う キャラクターのモデル Viewが扱う キャラクターのモデル

  63. 63 それぞれのモデルが適用される境界を明確にし、 境界内だけで適用されるモデルを作る。 境界を明示的にする事で、境界内を独立して開発可能になり、 他の箇所との結合度が減る。 6. 境界づけられたコンテキスト ? コンテキストが異なる キャラクター

    キャラクター キャラクター
  64. 64 7. レイヤードアーキテクチャ ・ユーザーインターフェース (プレゼンテーション) ・アプリケーション ・ドメイン ・インフラストラクチャー の4層に分ける 「エリック・エヴァンスのドメイン駆動設計」より引用

  65. 65 インフラストラクチャの位置を変更したバージョンを採用。 7. レイヤードアーキテクチャ ヴォーン・ヴァーノン 著 「実践 ドメイン駆動設計」より引用

  66. 66 実践ドメイン駆動設計で紹介されているバージョン。 ドメインオブジェクトを管理するインフラストラクチャ層 が、ドメイン層を参照できた方が都合が良かったため。 Assembly Definitionsを使って管理 7. レイヤードアーキテクチャ

  67. 67 各パターンがどの層にあてはまるか インフラストラクチャー層 • 基盤的な機能の実装 • 通信機能、DBアクセス、ローカルセーブデータなど の実装 • リポジトリの実装

    7. レイヤードアーキテクチャ
  68. 68 ユーザーインターフェース層 (プレゼンテーション層) • UI表示 • アウトゲームのMVC/MVPで構成された各画面 現プロジェクトでは、 MonoBehaviour継承クラスだからUI層、とはしていない。 そのクラスが提供している機能で判断している。

    各パターンがどの層にあてはまるか 7. レイヤードアーキテクチャ
  69. 69 アプリケーション層 • ドメインオブジェクト・基盤機能を使って、アプリ ケーション全体の調整・進行役を担う • ドメインとUI層の架け橋 各パターンがどの層にあてはまるか 現プロジェクトでは、ユーザー情報、キャラクター情報の管理、取 得、変更などを担う機能実装がある。

    機能毎にユースケース・クラスを作成し、そこに処理を記述してい る。 キャラクターをレベルUPさせる・装備を変更する・etc.. 7. レイヤードアーキテクチャ ゲームが持つ機能の実装
  70. 70 ドメイン層 • ドメインロジックの実装 • 値オブジェクト • エンティティ • ドメインサービス

    各パターンがどの層にあてはまるか システムの心臓部 7. レイヤードアーキテクチャ
  71. 71 ドメイン層が他のレイヤーと隔離されてい る事が重要。 他にもパターンは ・オニオンアーキテクチャ ・ヘキサゴナルアーキテクチャなどがあ る。 参考 : [DDD]ドメイン駆動設計で実装を始めるのに一番とっつきやすいアーキテクチャは何か

    https://qiita.com/little_hand_s/items/ebb4284afeea0e8cc752 7. レイヤードアーキテクチャ
  72. 72 Assembly Definitionsで定義 メリット 相互参照を無くして依存関係が単純になる レイヤードアーキテクチャの実装

  73. 73 レイヤードアーキテクチャを厳密に運用するのは結構難しい。 特にAssembly Definitionsを使って厳密に依存関係をコントロールする場合。 上から下へ、単一の方向にしかアクセス出来なくなる。 とはいえ逆方向にあるレイヤーの機能を使いたい事はある。 現プロジェクトでの解決方法を紹介します。 レイヤードアーキテクチャの実装

  74. 74 依存関係逆転の原則 レイヤードアーキテクチャの実装 具象 ではなく、抽象 だけを参照するようにする 具象 → 実装クラス 抽象

    → interfaceやabstractなどの抽象宣言 <参考> Robert C.Martin,角 征典,高木 正弘. Clean Architecture 達人に学ぶソフトウェアの構造と設計
  75. 75 依存関係逆転の原則 レイヤードアーキテクチャの実装 Presenter Application 上層から下層への依存関係しか存在しなくなる ソースコードの依存方向 (※ 処理の流れではない) Assembly

    Definitionsを設定 UIレイヤー (プレゼンテーション層) アプリケーション層
  76. 76 レイヤードアーキテクチャの実装 下層から上層のクラスを参照する事はできない。 しかし、上層に対して処理を実行したい。 Presenter Application 依存関係逆転の原則

  77. 77 レイヤードアーキテクチャの実装 アクセス可能な階層のinterfaceに依存 するようにする。 依存関係逆転の原則 Presenter Application IOutput OK OK

  78. 78 実際の処理の流れ と、ソースコードの参照方向が逆になる レイヤードアーキテクチャの実装 Presenter Application Presenter Application IOutput 処理の流れ

    ソースコードの参照方向 依存関係逆転の原則
  79. 79 しかし、結局は上層のインスタンスを取得する必要がある。 では、どう取得するのか? Abstract Factory、Service Locatorなどの解決方法があるが、 現プロジェクトでは後述のDIコンテナを使用した。 レイヤードアーキテクチャの実装 依存関係逆転の原則 Presenter

    Application IOutput 実体であるviewは、 どうやって取得するのか?
  80. 3. DIコンテナとUnity適用までの具体的な解説

  81. 81 多用されがちだったsingletonを使いたくない。singletonだと・・ • global変数 • 依存関係が見えづらい • 具象クラスに依存するため依存関係逆転の原則が使えない • Interfaceを使った実装の入れ替えが難しい

    経緯
  82. 82 Interfaceを使った実装の入れ替え とは 状況に応じて、実装A と 実装B を 切り替える 経緯 Interface

    実装 A 実装 B
  83. 83 外部から必要とするインスタンスを渡すのがDI (Dependency Injection) (「DIコンテナ」ではなく、 ただの「DI」) DIコンテナとは これを自動でやってくれるのが 「DIコンテナ」

  84. 84 Zenject(exenject) https://github.com/modesttree/Zenject リポジトリにあるサンプルコードを引用して説明します。 DIコンテナとは

  85. 85 DIコンテナとは 階層構造の一番下

  86. 86 DIコンテナとは 上から渡してあげる

  87. 87 DIコンテナとは 渡すものを作る必要がある

  88. 88 この繰り返しが、ずっと上の階層まで続いてしまう DIコンテナとは 上から渡してあげる

  89. 89 最終的に、上記のような依存関係の解決だけを担ってくれるものが必要になる。 上記のような処理を自動で解決してくれるのがDIコンテナ。 (誰が何を必要としているのか・生成の順序などを管理。生成知識を一手に担う黒子のような存在) DIコンテナとは 48. GoFデザインパターンとDI (前編) w/ twada

    https://fukabori.fm/episode/48
  90. 90 Unityでの実際の使用イメージ ISomeServiceとして、SomeServiceを登録

  91. 91 Unityでの実際の使用イメージ

  92. 92 • 各種サービスの連携 • レイヤーを跨いだ参照の受け渡し • 逆方向のレイヤーにある実装を使用する場合は、 interfaceを置く場所で制御 プロジェクトでの使い方 依存関係逆転の原則を活用

    Interface 実装 ここにinterfaceを置く この層の実装を使いたい
  93. 93 Interfaceに依存する事で、実装の差し替えを行う事ができる。 実装の差し替え インターフェース IOutput 通常UI UI非表示 log出力のみ 例「デバッグ用に、UI非表示でlog出力だけ行う高速周回モードの実装」 通常時

    デバッグ時 プロジェクトでの使い方
  94. 94 DIコンテナを導入するとどう変わるのか クラス間の依存関係把握が簡潔になる・アクセスコントロールがやりやすい あるクラスの機能を使うためにはコンスタントラクタで宣言する必要があるため、 依存関係が見えやすい。 不要なクラスの利用がなくなり、役割分担も明確になる。 Singletonはどこからアクセスされるのか把握が難しい。

  95. 95 落とし穴 ”DIコンテナ上で管理されているオブジェクト同士を、気の向くまま思うままに関連付けします。DIコン テナ上に管理されていないければ有り得ないような依存性であっても気にせず依存させるため、図8の ように依存性が網の目のようになります。” 網の目依存 DIコンテナの本当の使いどころ https://www.ulsystems.co.jp/topics/025 より引用

  96. 96 落とし穴 網の目依存はやりがちなのでは。 あらゆるクラスをDIコンテナに登録、自由に参照を取得する (全てがsingleton / 全てstaticクラスな事と同じ)

  97. 97 落とし穴 DIコンテナに登録するクラスは良く考慮する 現プロジェクトではサービスやリポジトリのRootインスタンスのみがDIコンテナに 登録可能。 Root要素から下は参照関係の解決にDIコンテナは使わない。 参照の受け渡し(ただのDI)で解決している Easyのためではない。simpleにするために使う

  98. 98 ライブラリ Zenject (extenject) を採用 Zenjectの固有機能はあまり使わず、他のDIコンテナライブラリに簡単に乗り換えら れるようにしている

  99. 99 ライブラリ •プロジェクト起動時、シーン読み込み直後の初期化の際にDIコンテナに詰める・取 得するだけ •シーン初期化以降、動的に生成されるinstance/prefabにはinjectionしていない •シーンはタイトル画面・アウトゲーム・インゲームのような粒度で分けている •基本はコンストラクタ インジェクション •mono behaviorに対してはメソッド

    インジェクション マイルドな使い方
  100. 4. その他の設計手法の紹介

  101. 101 • ロジックが書かれた集約・Entity・値オブジェクトなどのユニットテスト • ドメインモデルとして纏める事でテストしやすくなる • ライブラリ的な機能のテスト これらはUnityのTest Runnerを使い、jenkinsで定期実行 テスト

  102. 102 関数の呼び出し元を顧客、呼ばれる関数を供給者と見立てる。 顧客と供給者の間に契約が結ばれている、というメタファー。 「顧客が契約を守るなら、供給者は仕事を保証する」 具体的には? 契約による設計 オブジェクト指向入門 第2版 原則・コンセプト バートランド・メイヤー

    (著), 酒匂 寛 (翻訳)
  103. 103 下記の3つの条件を満たすようにコードを記述する 契約による設計 事前条件 関数の開始時に、関数を呼ぶ側で保証すべき条件 事後条件 関数が終了時に保証すべき条件 不変条件 そのオブジェクトが常に満たすべき条件 コードにすると?

    参考URL https://developer.hatenastaff.com/entry/2016/09/01/163542
  104. 104 契約による設計 参考URL : 契約による設計、例外、表明の関係について個人的なまとめ https://qiita.com/hiko1129/items/f312212070716f672ff6 こういった四角形を表すクラスがあるとする

  105. 105 契約による設計 参考URL : 契約による設計、例外、表明の関係について個人的なまとめ https://qiita.com/hiko1129/items/f312212070716f672ff6 事前条件 ・処理の最初に引数のチェック 事前条件

  106. 106 契約による設計 参考URL : 契約による設計、例外、表明の関係について個人的なまとめ https://qiita.com/hiko1129/items/f312212070716f672ff6 事後条件 ・関数の最後で結果・returnする値のチェック 事後条件 ・処理の最後にreturnする値をチェック

    事後条件
  107. 107 契約による設計 参考URL : 契約による設計、例外、表明の関係について個人的なまとめ https://qiita.com/hiko1129/items/f312212070716f672ff6 不変条件 ・このobjectが守るべき条件をチェック 不変条件

  108. 108 ドメイン駆動設計でも「表明(Assertion)」としてパターンが紹介されている。 現プロジェクトでは、これから充実させていくところ。主にドメインロジック をチェック。 契約による設計

  109. 109 小さい単位で独立してテスト可能なように作る 単体で動くように作る とても重要。最初期からこれを念頭に作る。 ゲームを最初から立ち上げずに、最小ではprefab単位で挙動のチェックが出来るように 結合度が低く、使い回しが効くパーツが作られる

  110. 110 アウトゲームの各画面・ポップアップなどに関して 単体で動くように作る とても重要。最初期からこれを念頭に作る。 ゲームを最初から立ち上げずに、最小ではprefab単位で挙動のチェックが出来るように MVPのView部分だけ切り出してテスト可能 View部分を単独で作り込める 現プロジェクトの一例

  111. 111 インゲームのviewテスト用シーン テスト項目をinspecterに表示。 ボタン押下でviewの挙動をチェックできる。 単体で動くように作る

  112. 112 単体で動くように作る Inspecterにボタンを表示するのはアセット「Odin」を使用 インゲームのviewテスト用シーン

  113. 113 prefabの構造規約 ドメイン駆動設計における集約の考え方を適用する • Prefabパーツへのアクセスはroot要素のコンポーネントを経由する • inspecter、Find()、GetComponentInChildren()等でprefabのroot要素を飛び越 えて中身の参照を取得しない Subsystem内に、 外から自由にアクセスしている

    窓口を通してしかアクセスできない
  114. 114 prefabの構造規約 UnityにはEasyにするための仕組みが沢山ある。 しかし敢えて使わない。 デバッグ機能やモックシーンは使っても良い (EasyでOK) Subsystem内に、 外から自由にアクセスしている 窓口を通してしかアクセスできない

  115. 5. 実装の詳細 例外・つまづいた点・悩みごと

  116. 実装においての例外

  117. 117 DIコンテナだと困るもの UIのボタンクラスなど、UnityのHierarchyツリー上で下層に置かれるようなUIパー ツなど。 末端オブジェクトからDIコンテナに入っているオブジェクトにアクセスしたい場合。 DIコンテナでの例外パターン SoundManager.Instance.PlaySe(); こういった機能は割り切ってsingleonにしている。 例: ボタンを押したら音を鳴る

  118. 118 Observerパターン ・ イベントの発行 ゲーム固有のイベントを発行・管理するものもsingletonにしている DIコンテナでの例外パターン サウンド再生もイベントで表現できるが、単独で使用される事が多いため敢えて分けている Observer.Instance.Notify("XxxEvent"); どんなイベントが発行されるか? たとえばps4のトロフィー獲得表示。何かを達成したタイミングで表示されるもの

  119. つまづいた点

  120. 120 値オブジェクト等のImmutableなクラスに変更。 ただ情報を他のレイヤーに運ぶだけのオブジェクトであれば、 DTO(Data Transfer Object)として表現。 現プロジェクトではDTOもImmutableとして作っている。 とりあえず色々Entityにされがち ドメイン駆動設計 -

    つまづいた点 生成後に変化しない物もEntityとして作ってしまった。 対処
  121. 121 当初、マスターデータが管理されたDatabaseの1レコードを表すクラスをEntity として表現したが、ゲーム中にマスターデータが変化する事はない。 そのためEntityとしての扱いはやめ、Immutableなクラスとして置き換えた。 とりあえず色々Entityにされがち ドメイン駆動設計 - つまづいた点 生成後に変化しない物もEntityとして作ってしまった。 対処

  122. 122 Entity・値オブジェクトにドメインロジックが書かれず、サービスに手続き型で書 かれてしまう ドメインモデル貧血症 ドメイン駆動設計 - つまづいた点 OOPを意識して書く。 チームでモデリングの技量を上げる必要がある。継続的に勉強会を実施。 対処

  123. 123 例えばUser情報管理オブジェクトなど。 複数のEntityや値オブジェクトから出来る集約は、直接中身のエンティティ等を触り たくなる。 集約の境界が曖昧で、集約内部に直接アクセスを許してしまう ドメイン駆動設計 - つまづいた点

  124. 124 面倒だが、ここは必要経費と捉えてRootを通したアクセスのみ認めるように変更。 情報を伝えるためのバケツリレーのようなメソッドがたくさん出来てしまうが、ここ は我慢。 (ここもeasy vs simpleで、simpleを採用する話) 集約の境界が曖昧で、集約内部に直接アクセスを許してしまう ドメイン駆動設計 -

    つまづいた点 対処
  125. 125 集約の境界が曖昧で、集約内部に直接アクセスを許してしまう ドメイン駆動設計 - つまづいた点 理想的には全てのアクセスで集約Rootを通すようにしたい。 ただ、それだと実装が煩雑になる部分も出てくるので、部分的に許可している。 内部の値を一時的に外部から参照するだけならOK 境界内のオブジェクトは互いに参照を保持し合ってOK OK

    集約内部に持つインスタンスのメソッドを、外部から実行するのはNG NG
  126. 126 集約の境界が曖昧で、集約内部に直接アクセスを許してしまう ドメイン駆動設計 - つまづいた点 基本的にはデメテルの法則を適用する。 クラスCのメソッドfは、次のオブジェクトのメソッドのみを呼び出すべき • Cそのもの •

    fで生成されたオブジェクト • fの引数で渡されたオブジェクト • Cのインスタンス変数に保持されたオブジェクト fは、上記の許されたメソッドから返されたオブジェクトのメソッドを呼び出し てはいけません。つまり友達とのみ会話し、知らない人とは会話してはいけない のです。 Robert C.Martin,花井 志生. Clean Code アジャイルソフトウェア達人の技 (Japanese Edition) (Kindle の位置No.2626-2631). Kindle 版.
  127. 127 集約の境界が曖昧で、集約内部に直接アクセスを許してしまう ドメイン駆動設計 - つまづいた点 ただし、デメテルの法則を適用するのは振る舞いを持つオブジェクトに対してのみ。 振る舞いを持たないデータ構造は内部を公開しても構わない。 データ構造に過ぎず、何ら振る舞いを持たないのであれば、これらは内部構造をごく自然に公開しており、デメテルの法則は 適用されません。 Robert

    C.Martin,花井 志生. Clean Code アジャイルソフトウェア達人の技 (Japanese Edition) (Kindle の位置No.2654-2655). Kindle 版.
  128. 128 ドメイン駆動設計 - つまづいた点 集約のルールを徹底するだけでスパゲティコードになる可能性はグッと下がる。 とても重要。 Subsystem内に、 外から自由にアクセスしている 窓口を通してしかアクセスできない

  129. 129 本来、リポジトリは集約の単位で作成される。 しかし集約内部にあるEntity単位でもリポジトリがある状態だった。 集約の内側パーツをリポジトリが持つことになってしまう。 リポジトリの粒度が集約単位ではない ドメイン駆動設計 - つまづいた点 モデリングが上手くいっておらず、集約の範囲が曖昧だったのが原因。集約の境界 をしっかり定義し、その単位で整理しなおす

    対処
  130. 悩んでいる点

  131. 131 値オブジェクト - Equals()、GetHashCode()の実装について メンバ変数が少ない場合は大丈夫だが、多い場合は実装が大変。 ドメイン駆動設計 - 悩み

  132. 132 値オブジェクトの実装 | Microsoft Docs https://docs.microsoft.com/ja-jp/dotnet/architecture/microservices/microservice-ddd-cqrs-patterns/implement-value-objects 値オブジェクト基底クラスを作って運用する方法がMSドキュメントにて紹介され ているが、パフォーマンス面で不安は残る。 値オブジェクト -

    Equals()、GetHashCode()の実装について ドメイン駆動設計 - 悩み
  133. 133 C#9のinitキーワード、record型が来れば楽になりそう。 Unityでも早く使えますように 現状、数が少なければ手動で記述。 数が多い場合はIDEの自動生成を使うのが楽だが、メンバ変数が増えた際、忘れず に生成しなおす必要がある。 そのため、タプルを使った実装も検討中。 値オブジェクト - Equals()、GetHashCode()の実装について

    ドメイン駆動設計 - 悩み 対処
  134. 134 Equals()が自動的に実装されるが、継承が出来ない。 パフォーマンスの懸念も。 値オブジェクトの実装について - structを使うとどうなるのか? このような理由で、現プロジェクトではクラスを使っている ドメイン駆動設計 - 悩み

  135. 135 大きな集約をどこまで許容するか • 分割した場合、整合性・不変条件をどう保っていくか ある概念が情報を大量に保持しており、どれも密接に関係していて切り離すのが難 しいケースがよくある。 ドメイン駆動設計 - 悩み

  136. 136 大きな集約をどこまで許容するか • 分割した場合、整合性・不変条件をどう保っていくか 参考 DDDで複数集約間の整合性を確保する方法(サンプルコードあり)[ドメイン駆動設計] https://little-hands.hatenablog.com/entry/2021/03/08/aggregation トランザクション単位で分割する手法があるが、 UnityのクライアントコードではDBが絡むトランザクションが関係ない事も多い。 その場合は大きいままでも良い?

    モデリングが上手く行っていないのかも? まだ明確な答えは出ておらず、いったん大きいまま扱っている。 ドメイン駆動設計 - 悩み
  137. 137 ドメイン駆動設計の導入直後、 ゲームに必要な汎用機能の実装をどのレイヤーに置くのか悩んだ。 特にUnityの機能を使った、Viewと関連する部分。 Unityでの実装がどのレイヤーになるのか ? ドメイン駆動設計 - 悩み

  138. 138 ユースケースの実現(アプリケーションが提供する機能)と捉え、現状アプリケーション層で 実装されている。 しかしドメインと関係の無い基盤機能と捉える方が適切なため、実装はインフラストラク チャ層、その機能を使う処理がアプリケーション層にある、という形の方が適切だった Unityでの実装がどのレイヤーになるのか ドメイン駆動設計 - 悩み 汎用機能

    ・アセットバンドルを読み込む ・ポップアップを表示 ・シーンの読み込み、画面遷移させる、など ・音を出す ?
  139. 139 SoundManager.Instance.PlayBgm(); 実装クラス指定アクセスになる。 interfaceを使った依存関係逆転の原則も使えない。 ServiceLocatorを使ってsingletonを纏める手法を検討中。 しかし一部singletonになっているものは、気軽にレイヤーを変更できない ドメイン駆動設計 - 悩み

  140. 140 適切なレイヤーはどこなのか 未だに悩む。 とはいえドメインとそれ以外を分離する事が最も大切。 そこ以外は厳密に考えなくても良いのかも? ドメイン駆動設計 - 悩み

  141. 6. まとめ

  142. 142 ドメイン駆動設計 良いところ • DDDを導入する事でチーム内で統一された設計思想を共有できるようになった • 悩んだ時 / 説明する際に参照できるリソースが豊富 •

    データと振る舞いがセットで記述される事でコードの保守性・可読性が向上する • ドメインへの理解が推奨される • 一度習得すれば所属プロジェクトが変わっても様々な面で力になる • UnityでなくてもOK。特定の技術にあまり左右されない
  143. 143 ドメイン駆動設計 • 導入のハードルはなかなか高い • 継続的な学習は必要 • チーム内に浸透するまで時間がかかる • まずはプログラムの実装パターン適用からでも効果はある

    • それでも各実装パターンへの正確な理解は必要 • ドメイン駆動の名の通り、ドメインへの理解を深める事が本質 • しかし形から入る事で分かる事も多い 大変なところ・注意したいところ
  144. 144 DIコンテナ 良いところ • singletonの代わりとなる • 多態性を使ったモック作成がやりやすい • 依存関係を管理しやすい 大変なところ

    • 最初の学習コスト • 結局やってる事は単純ながら、概念を掴むのが難しい
  145. 145 ドメイン駆動設計 と DIコンテナ 2つは非常に相性が良い。 レイヤードアーキテクチャを実践するうえで役に立つ。 まとめ 双方とも、アプリの種類や使用している技術に左右されずらく、 汎用的な知識・手法として役に立つ。 設計するうえで、非常にオススメです。

  146. None
  147. 147 エリック・エヴァンスのドメイン駆動設計 https://www.amazon.co.jp/dp/B00GRKD6XU ドメイン駆動設計入門 ボトムアップでわかる!ドメイン駆動設計の基本 https://www.amazon.co.jp/dp/B082WXZVPC 実践ドメイン駆動設計 https://www.amazon.co.jp/dp/B00UX9VJGW ドメイン駆動設計 モデリング/実装ガイド

    https://booth.pm/ja/items/1835632 Clean Code アジャイルソフトウェア達人の技 https://www.amazon.co.jp/dp/B078HYWY5X Clean Architecture 達人に学ぶソフトウェアの構造と設計 https://www.amazon.co.jp/dp/B07FSBHS2V オブジェクト指向入門 第2版 原則・コンセプト https://www.amazon.co.jp/dp/4798111112 参考書籍
  148. 148 DDDスコアカード https://www.informit.com/articles/article.aspx?p=1944876&seqNum=2 Domain Driven Design(ドメイン駆動設計) Quickly 日本語版 https://www.infoq.com/jp/minibooks/domain-driven-design-quickly/ DDD

    Reference - Domain Language https://www.domainlanguage.com/ddd/reference/ DDDリファレンス 定義とパターン概要 (鋭意修正中, CC-BY) https://zenn.dev/takahashim/books/fb4cdc32f8e95c ドメイン駆動設計をゲーム開発に活かす https://www.slideshare.net/masuda220/ss-111011089 ゲームで学ぶ「役に立つ」ドメインモデルの考え方 - Qiita https://qiita.com/MinoDriven/items/7b4609d0bc6717769060 DDD難民に捧げる Domain-Driven Designのエッセンス 第1回 ドメイン駆動設計とは https://www.ogis-ri.co.jp/otc/hiroba/technical/DDDEssence/chap1.html より引用 Facade Pattern as the way to implement Aggregate http://rubyblog.pro/2017/05/facade-pattern 参考資料URL
  149. 149 [DDD]ドメイン駆動設計で実装を始めるのに一番とっつきやすいアーキテクチャは何か https://qiita.com/little_hand_s/items/ebb4284afeea0e8cc752 DIコンテナの本当の使いどころ https://www.ulsystems.co.jp/topics/025 48. GoFデザインパターンとDI (前編) w/ twada

    https://fukabori.fm/episode/48 契約による設計の紹介 - Hatena Developer Blog https://developer.hatenastaff.com/entry/2016/09/01/163542 契約による設計、例外、表明の関係について個人的なまとめ https://qiita.com/hiko1129/items/f312212070716f672ff6 値オブジェクトの実装 | Microsoft Docs https://docs.microsoft.com/ja-jp/dotnet/architecture/microservices/microservice-ddd-cqrs-patterns/implement-value-objects DDDで複数集約間の整合性を確保する方法(サンプルコードあり)[ドメイン駆動設計] https://little-hands.hatenablog.com/entry/2021/03/08/aggregation 参考資料URL