Slide 1

Slide 1 text

TECH×GAME COLLEGE#28 形から⼊ったドメイン駆動設計によるゲーム開発の光と闇 2019年10⽉23⽇ (⽔) 株式会社Nextat 中榮健⼆ Nextat Inc. 1

Slide 2

Slide 2 text

お詫びと訂正 本⽇はDDDの核⼼部分の話はしません この煽り⽂の答えが出ないことをお詫びして訂正いたします Nextat Inc. 2

Slide 3

Slide 3 text

⾃⼰紹介 京都から来ました - 中榮健⼆ (なかえけんじ) - twitter: @n_1215  - 株式会社Nextat 取締役 - baserCMS コアコミッター(最近コミットしてない) - Laravel + Unity でソシャゲ開発など Nextat Inc. 3

Slide 4

Slide 4 text

発表概要 1. ドメイン駆動設計の概要と軽量DDD 2. レイヤ・クラス分割の話 3. ユビキタス⾔語が統⼀されなかった話 4. 10分経っても返ってこないガチャAPIのチューニングの話 5. 境界づけ(られなかっ)たコンテキストの話 6. ちょっと深いモデルの話 Nextat Inc. 4

Slide 5

Slide 5 text

参加者の皆様に質問 ゲーム業界の⽅? 趣味でゲームを開発されている⽅? サーバサイドの開発者の⽅? ソシャゲをやったことがある⽅? Nextat Inc. 5

Slide 6

Slide 6 text

DDDについて 聞いたことがある⽅? 開発に取り⼊れている⽅? Evans本を読んだことがある⽅? Nextat Inc. 6

Slide 7

Slide 7 text

Domain Driven Design (ドメイン駆動設計)の概要 Nextat Inc. 7

Slide 8

Slide 8 text

DDDの概要 開発者・顧客や業務の専⾨家を含む関係者の間で、共通の⾔葉とメンタルモデル を育てシステムを開発していく⼿法 戦略 共通の⾔葉を作り業務をドメインモデルとして表現する 戦術 設計・実装の技術的パターン、オブジェクト指向のベタープラクティス Nextat Inc. 8

Slide 9

Slide 9 text

⽤語 ドメイン ソフトウェアを作る対象となる問題領域 モデル 事象の特定の側⾯を抜き出し簡略化した模型のようなもの。 ex. 数理モデル ドメインエキスパート 問題領域に詳しい専⾨家 ユビキタス⾔語 ドメインモデルを表現するためのチームメンバ全員によって使⽤される共通の⾔語 Nextat Inc. 9

Slide 10

Slide 10 text

DDDの根本の仮定 ドメインエキスパートと協⼒してドメインを理解し、モデルを継続的に改善する この仮定が成⽴しない開発現場もある Nextat Inc. 10

Slide 11

Slide 11 text

開発がイテレーティブではない とか Nextat Inc. 11

Slide 12

Slide 12 text

上流から表計算ソフトで書かれた設計書だけが流れてくる とか Nextat Inc. 12

Slide 13

Slide 13 text

ドメインエキスパートと開発者が対話できる環境がない とか Nextat Inc. 13

Slide 14

Slide 14 text

ドメインエキスパートがそもそもいない とか Nextat Inc. 14

Slide 15

Slide 15 text

現実は厳しい 組織やプロセスの根本の改善から始める??? 開発者⾃⾝がドメインエキスパートになる??? DDDを実践することができないのか!? Nextat Inc. 15

Slide 16

Slide 16 text

軽量DDD(戦術的DDD) Nextat Inc. 16

Slide 17

Slide 17 text

軽量DDD(戦術的DDD) DDDの戦術的パターンの⼀部だけをつまみ⾷いする⽅法 戦術的パターンの例 レイヤ化アーキテクチャ Repository、Factory、Entity、Value Object Speciaficationパターン Nextat Inc. 17

Slide 18

Slide 18 text

軽量DDDへの批判、失敗談 戦術的DDD、軽量DDDと聞くと⼀⾒聞こえはいい 悪く⾔えば形から⼊ったDDDもどき 戦略的なドメインモデリングを取り⼊れた時のようなメリットは薄い "○○FWでDDD"などと発⾔すると反応が冷たいのはこのせい Nextat Inc. 18

Slide 19

Slide 19 text

今⽇は軽量DDDを中⼼にした話をします メリットが薄いとは⾔え、効果がないわけではない 戦術的パターンの解説だけでも結構な分量がある オブジェクト指向設計の指針として 開発者が触る部分だけでも歴戦の開発者の知⾒を参考にできる 戦略的ドメインモデリングを実践する前の基礎として 戦術を理解することで細部に捕らわれることがなくなり、より重要なモデリング の話に集中できる Nextat Inc. 19

Slide 20

Slide 20 text

お話しするソーシャルゲームの想定 キャラクターの育成 ガチャによるキャラクター、装備品の⼊⼿ ストーリーを進めてバトル タップ中⼼の簡単なUI Nextat Inc. 20

Slide 21

Slide 21 text

お話しするゲームシステムの主な構成 ゲームサーバ: PHP + Laravel ゲームAPI: Web, Android, iOSに対応するため、JSONを返すAPI マスタデータ管理画⾯: マスタデータ作成・更新のための機能。HTML CS管理画⾯: ユーザの⾏動履歴などお問い合わせ対応⽤の機能。HTML データベース: MySQL、Redis クライアント: Unity、 JavaScript Nextat Inc. 21

Slide 22

Slide 22 text

注1: "ドメインエキスパートがいない"などと煽っておいてアレで すが、関係者の皆さんとの話はしやすいプロジェクトでした 注2: ⼀部実際のコードや⽤語などを簡略化・変更して掲載してい ます Nextat Inc. 22

Slide 23

Slide 23 text

1. レイヤ・クラス分割の話(光:闇 = 3:7) Nextat Inc. 23

Slide 24

Slide 24 text

分割しない例:典型的なWeb MVCフレームワーク による開発 Model View Controller Nextat Inc. 24

Slide 25

Slide 25 text

Model Eloquent (ActiveRecord系のORM) の"モデル"を継承しただけ クエリ発⾏、DB保存、ドメインロジックを全て⾏う class UserCharacter extends \Illuminate\Database\Eloquent\Model { } Controller class UserCharactersController { public function lock(int $userCharacterId): JsonResponse { $userId = auth()->id(); $userChara = UserCharacter::whereUserId(userId)->findOrFail($userCharacterId); $userChara->is_locked = true; $userChara->save(); return response()->json($userChara->toArray()); // この辺りがView } } Nextat Inc. 25

Slide 26

Slide 26 text

定番の戦術1. レイヤ化アーキテクチャ レイヤ 責務 UI (Presentation) ユーザに情報を表⽰、ユーザのコマンドを解釈 Application ソフトウェアで⾏う仕事の定義。ドメインオブジェクトにより問 題を解決 Domain (Model) ビジネスの概念、情報、ビジネスルールを表現 Infrastructure 上位のレイヤを⽀える⼀般的な技術的機能を提供 Nextat Inc. 26

Slide 27

Slide 27 text

複雑なプログラムをレイヤに分割 各レイヤは下位層だけに依存 Evans本ではドメイン層を隔離し、ドメインオブジェクトがドメインモデルを表 現する事に専念させることを強調 Nextat Inc. 27

Slide 28

Slide 28 text

定番の戦術2. ドメイン、インフラ層のクラス達 エンティティ(Entity) 値オブジェクト(Value Object) サービス(Service) ファクトリ(Factory) リポジトリ(Repository) Nextat Inc. 28

Slide 29

Slide 29 text

エンティティ(Entity) 時間経過などによりその属性が変化しても同⼀とみなすオブジェクト 要は固有の識別⼦=IDを持つもの ex). ユーザ所持キャラクター Nextat Inc. 29

Slide 30

Slide 30 text

ex). ユーザ所持キャラクター(略式) class UserCharacter { /** ID */ private $userCharacterId; /** キャラクター(マスタ) */ private $character; /** 攻撃⼒などのステータス */ private $status; /** ロックされているかどうか */ private $isLocked; public function lock(): void { $this->isLocked = true; } } Nextat Inc. 30

Slide 31

Slide 31 text

値オブジェクト(Value Object, VO) その値だけが重要なモデルは値オブジェクトとして分類する 不変(Immutable)なものとして扱う ex) ユーザ所持キャラクターのレベル、レアリティ、名前 ... Nextat Inc. 31

Slide 32

Slide 32 text

ex) キャラクターのレアリティ class CharacterRarity { /** @var int */ private $value; public function __construct(int $value): void { if ($value < 1 || $value > 5) { throw new \InvalidArgumentException(' レアリティは1 以上5以下の整数'); } $this->value = value; } public function getValue(): int { return $this->value(); } } Nextat Inc. 32

Slide 33

Slide 33 text

ファクトリ(Factory) 複雑なドメインモデルの⽣成ロジックをカプセル化して内部の実装を隠蔽する 主としてドメインモデルの新規⽣成、永続化したデータからの再構成の2種類に分 かれる Nextat Inc. 33

Slide 34

Slide 34 text

ex) キャラクターのファクトリ class CharacterFactory { public function makeFromRecord(CharacterRecord $record): Character { return new Character( new CharacterId($record->id), new Name($record->name), new Rarity($record->rarity), // 略 ); } } Nextat Inc. 34

Slide 35

Slide 35 text

リポジトリ(Repository) Entityのデータストアへの永続化、およびデータストアからの取得・検索 Entityのコレクションをエミュレートし、メモリ上にあるかのように扱う 通常、Interfaceを⽤意してドメイン層からは永続化の詳細を気にさせない Nextat Inc. 35

Slide 36

Slide 36 text

ex) キャラクターのリポジトリ class CharacterRepository { public function find(CharacterId $characterId): ?Character { $record = CharacterRecord::query()->find($characterId->getValue())); if ($record === null) { return null; } return $this->characterFactory->makeFromRecord($record); } } Nextat Inc. 36

Slide 37

Slide 37 text

サービス(Service) ドメインの操作だが、EntityやVOの機能としてモデル化しにくいもの 名詞ではなく動詞的な名前を持つ ex) ユーザ所持キャラクターをロックする操作、強化する操作 Nextat Inc. 37

Slide 38

Slide 38 text

ex) ユーザ所持キャラクターをロックする操作 class LockUserCharacterService { public function lock(UserCharacterId $userCharacterId): UserCharacter { $userCharacter = $this->userCharacterRepository->find($userCHaracterId); $userCharacter->lock(); $this->userCharacterRepository->save($userCharacter); return $userCharacter; } } Nextat Inc. 38

Slide 39

Slide 39 text

整理し直す before after UI Controller,View Controller,View Application Controller ApplicationService Domain Eloquent Model Entity、VO RepositoryのInterface Factory DomainService Infrastructure Eloquent Model Repository(Eloquent実装) Nextat Inc. 39

Slide 40

Slide 40 text

Nextat Inc. 40

Slide 41

Slide 41 text

レイヤ、クラス分割実践時の(光) Eloquent (LaravelのORM)のFat Model化の緩和 POPOで実装したEntity、VOにドメインの機能が分離され明確になる ORMのクエリの発⾏箇所がある程度絞られた VOによる抽象レベルの統⼀と堅牢性 スカラーではなくVOを⾜場に。コードをドメインの⾔葉で書きやすくなる VOのコンストラクタで値の制約を表現。不正なオブジェクトの作成を阻⽌ 引数の順番間違い防⽌など型の恩恵で凡ミスが減少。IDEの静的解析との相性◎ Nextat Inc. 41

Slide 42

Slide 42 text

レイヤ、クラス分割実践時の(闇) クラス数の増⼤ 記述量の最⼩化を是とするFW使いとして育ってきていると実装するクラス数の増 加に慣れるまでに時間がかかる 最初のうちはクラスの関係性や全体の処理順序の把握が困難 各クラスの責務の混乱 どのクラスで何をするのか、何をしてはいけないのか共有しきれていなかった た Repository内にデータストアの影響を閉じ込めるはずが、FactoryやService内 でのクエリ発⾏も⾏われていた Nextat Inc. 42

Slide 43

Slide 43 text

貧⾎ドメインモデル クラスを分割すること⾃体に不慣れな内はEntityの設計まで気が回らない レイヤを分割してRepositoryを作ってEntityの属性をVOでラップした時点で⽴派 なDDDだと思っていた⼈も多数 DBレコードオブジェクトの中⾝を詰め替えただけの全Getter, Setter付きEntity Entityが不変条件を守れない 書いた本⼈の感想「無駄にクラスが増えただけでは。やはりEloquentこそ⾄⾼」 そらそうよ Nextat Inc. 43

Slide 44

Slide 44 text

フロントエンドの分割が過剰だった話 なぜかサーバ側より複雑……? Nextat Inc. 44

Slide 45

Slide 45 text

新規実装・修正のコストが嵩んだ 表⽰するだけの機能には過剰であった フロント側の処理の⽐重が⾼いドメインでは有効なこともあった ex. フロント側で装備品の強化後のパラメータをプレビュー、⾼速化とサーバ 負荷の減少を狙うケース サーバ側のアーキテクチャをそのまま持ってきても成功するとは限らない Nextat Inc. 45

Slide 46

Slide 46 text

View駆動開発の深い闇 Viewを元にバックエンドのAPIを考え始める UIに新しい項⽬が必要になると全部修正。層の無駄な多さがネックに。 先にバックエンドのEntityが持つ可能性のある項⽬を検討しておき、APIにも反映 しておく⽅が良かった Nextat Inc. 46

Slide 47

Slide 47 text

レイヤ分割により、各レイヤ内、特にドメインの実装に専念しや すくなる 各レイヤ、クラスの役割と意味を開発メンバーに共有しておかな いと形骸化する 過剰に分割すると⾟くなることもある。レイヤやクラスの責務と 必要性を考えるべき Nextat Inc. 47

Slide 48

Slide 48 text

2. ユビキタス⾔語が統⼀されなかった話(ほぼ闇) Nextat Inc. 48

Slide 49

Slide 49 text

Therefore its name was called Babel, because there the LORD confused the language of all the earth. And from there the LORD dispersed them over the face of all the earth. https://biblehub.com/esv/genesis/11.htm The city and its tower Nextat Inc. 49

Slide 50

Slide 50 text

ユビキタス⾔語と分業 仕様作成者、バックエンド開発、フロント開発、フロントUI開発などと分業して いた事例 ゲーム⽤語がレイヤ化と分業によりたやすく共有できなくなった Nextat Inc. 50

Slide 51

Slide 51 text

例1. 仕様書からしてそもそも揺れている 仕様書: キャラ or キャラクター or ユニット バックエンド: キャラクター、ところによりキャラ フロントエンド: キャラクター、ところによりユニット Nextat Inc. 51

Slide 52

Slide 52 text

例1の対策 仕様書を書く側でなるべく統⼀してもらう 仕様書を読んでいて揺れに気づいたら指摘 Nextat Inc. 52

Slide 53

Slide 53 text

例2. ⽤語の指す概念を正しく共有できていない キャラクター(マスターデータ) ユーザー所持キャラクター 仕様書 単に"キャラクター", "キャラクターID"とだけ⾔及される場合も多い ガチャの排出キャラクター (マスタ系) パーティに編成されたキャラクター(ユーザー所持系) Nextat Inc. 53

Slide 54

Slide 54 text

バックエンド 所持:UserCharacter マスタ:Character フロントエンド 所持:Character 特にIDの混乱によるバグが怖い Nextat Inc. 54

Slide 55

Slide 55 text

例2の傾向と対策 ⽤語集を作る 共有の場を設ける 開発中に⽂脈で判断できない場合は仕様を再確認 レビューで指摘する Nextat Inc. 55

Slide 56

Slide 56 text

例3. 英語名がバラバラ アイテムの数量 itemAmount itemQuantity itemCount など、 バックエンドのEntity フロントエンドのEntity フロントエンドのView で揺れているようなケースも Nextat Inc. 56

Slide 57

Slide 57 text

例3の傾向と対策 英語圏ではユビキタス⾔語を命名にそのまま利⽤できる 仕様書とプログラムに違う⾔語を使う我々のケースでは⽇英翻訳による差異が発 ⽣ 対訳表をしっかり作った⽅がいい。揺れを発⾒したら追加していく Nextat Inc. 57

Slide 58

Slide 58 text

おまけ:プログラムを⽇本語で書くのはあり? ⽇英翻訳が⾟いならそのまま⽇本語を使えばいいじゃない → いろいろ⾟い 複数形。characters => キャラクター達? キャラクターリスト? 真偽値を返すメソッドの命名 〜かどうか ⽇本語ファイル名で問題が起こるツールも Nextat Inc. 58

Slide 59

Slide 59 text

語順も違うし…… 英語:主語(S)動詞(V)⽬的語(0) The user character attacks to the enemy. $userCharacter->attack($enemy); ⽇本語: 主語(S)⽬的語(0) 動詞(V) ユーザー所持キャラクターが 敵を 攻撃する $ ユーザー所持キャラクター-> 攻撃する($ 敵) 助詞もないし⽚⾔っぽい Nextat Inc. 59

Slide 60

Slide 60 text

参考:なでしこ ⽇本語プログラミング⾔語 ⽇本語の語順に対応 https://nadesi.com/doc/kouza/01-1-hello.htm クジラが「こんにちは」と⾔う。 なでしこ3はブラウザで動作するらしい? その他の⽇本語プログラミング⾔語としてはドリトル、プロデルなどがある Nextat Inc. 60

Slide 61

Slide 61 text

開発者の間だけでも⽤語を正しく共有することは⼤事 Nextat Inc. 61

Slide 62

Slide 62 text

4. 10分経っても返ってこないガチャAPIのチューニングの 話 Nextat Inc. 62

Slide 63

Slide 63 text

とある開発初期のガチャ抽選API POST /gachas/123/roll Nextat Inc. 63

Slide 64

Slide 64 text

よーし、回しちゃうぞ〜 フロント HTTPレスポンスのステータスが500 PHP Fatal error: Maximum execution time of 30 seconds exceeded そうか時間が⾜りなかったか〜 Nextat Inc. 64

Slide 65

Slide 65 text

PHPのmax_execution_time設定を伸ばす nginx 504 Gateway Time-out Nextat Inc. 65

Slide 66

Slide 66 text

nginxとPHPの設定値を10分くらいに伸ばす ⻑いのでTwitterをしながら待つ nginx 504 Gateway Time-out だめだこりゃ Nextat Inc. 66

Slide 67

Slide 67 text

何が問題だったか 巨⼤な集約 柔軟すぎるマスタの仕様。対価の設定、排出率設定、出現確率アップ、確定設定 などガチャのマスタテーブルだけで20超え ガチャ = ガチャ内容物×1000個がそのままEntityに。純粋に⼤きい クエリ数もさることながら、DBレコードオブジェクトからEntityへの変換にもか なりの時間がかかり、不必要にメモリも⾷っていた DBクエリ Repository以外の場所で発⾏されている脱法クエリが多くあった レイヤを分離したことで、FactoryでN+1問題が発⽣していることも Nextat Inc. 67

Slide 68

Slide 68 text

パフォーマンスチューニングだっ レイヤ・クラスの再整理 クエリの発⾏を把握しやすいようにレイヤの役⽬を再整理 FactoryやServiceでのクエリ発⾏を原則禁⽌し、クエリ発⾏箇所をRepository に寄せる Repositoryでクエリ発⾏とFactoryの処理を最⼩にするプラクティスを共有。 ORMのEagerLoad機能を使わず、⼿作業で温かみのあるEntityレベルの EagerLoad Nextat Inc. 68

Slide 69

Slide 69 text

マスタデータはキャッシュ DBクエリレベルでのキャッシュ → イミュータブルなマスタEntityのキャッシュ へ ローカルのAPCuにキャッシュ デプロイ時にキャッシュのウォームアップ DBクエリ数、キャッシュ取得数を可視化して地道にチューニング Clockwork を拡張した管理画⾯でローカルでも確認できるように ORMレベルではなく、Repositoryレベルでのクエリ発⾏元の追跡。 Nextat Inc. 69

Slide 70

Slide 70 text

活躍していたClockwork Nextat Inc. 70

Slide 71

Slide 71 text

感想と教訓 ベストプラクティスが定まった後の新規実装はとても楽 ドメインから詳細を隠蔽しても開発者がRepositoryの内部を気にする必要はあ る。DBクエリレベルでの最適化も考える ⼀部クエリが散らばっている箇所が残っていて⾟い 何でもかんでも集約しすぎると重い。必要な側⾯だけを抜き出すことの重要性 チューニング結果 この時点で計測不能 → 数秒くらいまでは縮めた Nextat Inc. 71

Slide 72

Slide 72 text

その後、負荷試験チームによる再チューニングにより 無事実⽤レベルへ Nextat Inc. 72

Slide 73

Slide 73 text

Webフレームワークとの付き合い⽅の変化の話(光?) Nextat Inc. 73

Slide 74

Slide 74 text

Eloquent (ActiveRecord系ORM)との付き合い⽅ before: Eloquent DBレコードに⾃我が芽⽣えたオブジェクト。はじめにDBありき CRUDくらいしかないアプリならお⼿軽 永続化・クエリの機能とドメインのための機能が⼀つのオブジェクトに混在 規模が⼤きくなってくると、⻑期的に秩序を保ち続けるのは難しい after: Repository 考え⽅が逆。はじめにEntityありき Entityが永続化される際にデータストアにマッピングされる Repositoryの実装にEloquentが使われるが詳細はドメインからは隔離される Nextat Inc. 74

Slide 75

Slide 75 text

Eloquentに引きずられていたテーブル設計 before とりあえずauto incrementな整数サロゲートキー after マスタ系:決め打ち整数ID、集約のサブのモデルに対応するテーブルは親のIDを 含む複合主キー 所持系:とりあえずUUIDをバイナリで 永続化前からIDを持つほうが都合が良い Entityとテーブルは必ずしも1:1でなくていい Nextat Inc. 75

Slide 76

Slide 76 text

Controllerの役割の減少 before ソフトウェアの仕事を定める調整役 after ControllerはServiceの⼊出⼒をHTTPリクエスト・レスポンスにマッピングする だけ UIに関係ないServiceまでで⼀連の処理が完結している Nextat Inc. 76

Slide 77

Slide 77 text

ドメインファースト レイヤを分けたことで、FWに依存するUI、インフラ層抜きでドメイン層だけでテ ストが書けるように ⼀旦データストアのことを忘れてドメインオブジェクトから書き始めていく たまにDBマイグレーションファイルを書き忘れる 我々が設計したインターフェースがたまたまLaravelのコンポーネントを⽤いて実 装されているという感覚 フレームワークと喧嘩しない。フル活⽤は難しいが、必要な機能を Infrastructure・UIに活⽤する。 Nextat Inc. 77

Slide 78

Slide 78 text

DB、UIファーストからドメイン、オブジェクトファ ーストへ フレームワークを活⽤しても、ドメイン層の設計まで は渡さない Nextat Inc. 78

Slide 79

Slide 79 text

5. 境界づけ(られなかっ)たコンテキスト (ほぼ闇) Nextat Inc. 79

Slide 80

Slide 80 text

境界づけられたコンテキスト 同じモノを対象とした複数の機能において、違う属性を抽出してモデル化したい ケースがある モデルが複数存在するようなケースでは、混乱を防ぐためにスコープを明確化す ることが重要 アプリケーションの⽤途やチームに応じて境界を定め、コンテキスト内でのモデ ルの分裂を防ぐ ex) 輸送アプリケーションにおける予約コンテキストと輸送ネットワークコンテ キスト とあるプロジェクト 解決したい問題が根本的に異なる機能でも、共通のモデルで対応していた Nextat Inc. 80

Slide 81

Slide 81 text

例1. マスタデータ管理 ゲームの設定の調整を柔軟に⾏うための機能 マスタデータの⼊⼒ミスは結構な確率で起こる ⼊⼒内容のバリデーションが必須 ⼿法は様々 Excel⼊⼒ + 型定義DSLによるバリデーション Googleスプレッドシート管理 + ⼈の形をしたマスタ管理システム 専⽤のマスタデータ管理画⾯ Nextat Inc. 81

Slide 82

Slide 82 text

とあるプロジェクトのマスタデータ管理 マスタデータのWeb管理画⾯を作り⼊⼒フォームから更新。ほぼCRUDのみ ⼊⼒したデータはCSVやJSONに出⼒しコードとともにバージョン管理 ゲームAPIと同じドメインオブジェクトを利⽤していた ゲームAPIでは使わないメソッドがドメインオブジェクトに紛れ込んでくる GetterやSetterの追加によるカプセル化の破壊 Repositoryでのページネーション Serviceの依存関係の複雑化 ゲームのEntityとは違う粒度で⼊⼒画⾯を作りたいケース Nextat Inc. 82

Slide 83

Slide 83 text

対策と反省点 最終的には同じドメインオブジェクトは使ってもServiceを分ける⽅向性に コアドメインではないため省⼒化や分離を検討してもよかった 管理画⾯だけORMを直接利⽤ マスタ管理アプリケーションを分離 データの⼀括⼊⼒に不向きだった。やはり表計算ソフト⽅式が強い CSV⼀括インポートなど補助的な機能も作られた Googleスプレッドシート、Airtableなど既存のシステムの活⽤もやはり有⼒ な選択肢だった Nextat Inc. 83

Slide 84

Slide 84 text

例2. CS管理画⾯ ユーザの⾏動履歴を確認し問い合わせに対応 or デバッグに活⽤するための機能 CS管理画⾯でしか閲覧しない履歴のために、ゲーム側のServiceに履歴追加処理 が埋められる Nextat Inc. 84

Slide 85

Slide 85 text

ex) ユーザ所持キャラクターの限界突破の履歴 class BreakLimitUserCharacterService public function breakLimit( PlayerId $playerId, UserCharacterId $userCharacterId ) { // ( 略) // 限界突破の処理 $afterUserCharacter = $beforeUserCharacter->breakLimit($materials); $this->userCharacterRepository->save($afterUserCharacter); // 履歴を残す処理 $this->breakLimitHistoryService->add( $beforeUserCharacter, $afterUserCharacter, $materials ); // その他限界突破素材アイテムを消費する処理など } Nextat Inc. 85

Slide 86

Slide 86 text

ゲームとCS管理画⾯専⽤のアプリケーションの分離も可能だったはず ゲーム本体からCS管理画⾯アプリケーションにイベントで通知 厳密なリアルタイム性が要求されない機能なので結果整合性があれば問題ない Nextat Inc. 86

Slide 87

Slide 87 text

6. ちょっと深いモデル(ほぼ光) Nextat Inc. 87

Slide 88

Slide 88 text

アイテム、装備、キャラクターをユーザに付与する機能 ユーザへのミッションの報酬、バトルのドロップ、ショップ商品購⼊で頻出 プレゼントボックスに送る、直接ユーザの持ち物として付与する 新規⼊⼿かどうかの判定を⾏う ユーザの所持数の上限超過判定を⾏う Nextat Inc. 88

Slide 89

Slide 89 text

コードの重複 ユーザに付与されるデータ アイテムマスタ + 有効期限 + 数 装備品マスタ + 初期経験値 + 数 キャラクターマスタ + 初期経験値 + 初期限界突破数 + 数 switchで分岐してそれぞれ処理 あまりに多すぎて実は共通化したほうがいいことに気づく Nextat Inc. 89

Slide 90

Slide 90 text

ユーザが受け取れるもの、という視点で抽象化 UserReceivableInterfaceと命名、専⽤のServiceを使ってまとめて受け取れるよ うにした 開発者に使い⽅を周知。チーム全員のユビキタス⾔語とまではならなかった。 抽象レベルの改善。クライアントコードではユーザに付与するものがアイテムか 装備品かキャラクターかをほとんどきにする必要がなくなった 重複するコードの排除 Nextat Inc. 90

Slide 91

Slide 91 text

軽量DDDを実践した感想とまとめ Nextat Inc. 91

Slide 92

Slide 92 text

感想とまとめ ゲームのための設計・インターフェースをFWの機能ありきではない形で考えられ るようになる POPOのクラスを数多く作るとオブジェクト指向に慣れてくる 戦術的パターンをただ適⽤するだけでなく、そのパターンの役割を考えると良い 悩んだことがEvans本を読み返すと書いてある。5章6章あたりだけでも参考にな る。 ゲームはもともとコンピュータのために作られているので開発者と仕様の距離が 近く、モデリングしやすいかも Nextat Inc. 92

Slide 93

Slide 93 text

軽量DDDも捨てたもんじゃないぞ! Nextat Inc. 93

Slide 94

Slide 94 text

PR: We're hiring!! Nextat Inc. 94

Slide 95

Slide 95 text

Nextatではエンジニアを募集中! 最近のメインは業務システム、ECサイト、ソーシャ ルゲームなど ⼀緒に設計について議論してくれる⽅を募集中 Nextat Inc. 95

Slide 96

Slide 96 text

え、でも京都でしょ? 本社は京都ですが、東京にもオフィスができました 沖縄進出も検討中 Nextat Inc. 96

Slide 97

Slide 97 text

ご清聴ありがとうございました この後の質疑応答やmeet upでも質問受付 お⼿柔らかに Nextat Inc. 97