Upgrade to Pro — share decks privately, control downloads, hide ads and more …

形から入ったドメイン駆動設計によるゲーム開発の光と闇

n1215
PRO
October 23, 2019

 形から入ったドメイン駆動設計によるゲーム開発の光と闇

TECHxGAME COLLEGE #28(https://techxgamecollege.connpass.com/event/148035/) の発表資料です

n1215
PRO

October 23, 2019
Tweet

More Decks by n1215

Other Decks in Programming

Transcript

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  25. 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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  32. 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

    View Slide

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

    View Slide

  34. 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

    View Slide

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

    View Slide

  36. 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

    View Slide

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

    View Slide

  38. 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

    View Slide

  39. 整理し直す
    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

    View Slide

  40. Nextat Inc. 40

    View Slide

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

    View Slide

  42. レイヤ、クラス分割実践時の(闇)
    クラス数の増⼤
    記述量の最⼩化を是とするFW使いとして育ってきていると実装するクラス数の増
    加に慣れるまでに時間がかかる
    最初のうちはクラスの関係性や全体の処理順序の把握が困難
    各クラスの責務の混乱
    どのクラスで何をするのか、何をしてはいけないのか共有しきれていなかった

    Repository内にデータストアの影響を閉じ込めるはずが、FactoryやService内
    でのクエリ発⾏も⾏われていた
    Nextat Inc. 42

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  49. 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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  57. 例3の傾向と対策
    英語圏ではユビキタス⾔語を命名にそのまま利⽤できる
    仕様書とプログラムに違う⾔語を使う我々のケースでは⽇英翻訳による差異が発

    対訳表をしっかり作った⽅がいい。揺れを発⾒したら追加していく
    Nextat Inc. 57

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    Nextat Inc. 62

    View Slide

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

    View Slide

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

    View Slide

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

    Nextat Inc. 65

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  69. マスタデータはキャッシュ
    DBクエリレベルでのキャッシュ → イミュータブルなマスタEntityのキャッシュ

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

    View Slide

  70. 活躍していたClockwork
    Nextat Inc. 70

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  85. 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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide