MPがありません。

$liiu->mp == 0

ドメイン駆動設計の実践 勉強会メモ

8/22に開催された【TECH x GAME COLLEGE #1】ドメイン駆動設計の実践 - connpassに参加した時のメモです。
とても勉強になる面白い話を聞かせていただきました。

アジェンダ

  • DDDの基礎知識
  • ゲーム開発への活用の可能性
  • ゲーム開発に取り入れる時の障害

DDDの基礎知識

ソフトウェア設計の一般原則

  • 関心の分離
  • モジュール構造

全体を複数の関心事に分離し、それぞれの関心事を独立したモジュールとして開発する。
そしてそれらを組み合わせて全体を構築する。

DDDの特徴

DDDではどのように関心事を分離しモジュール構造を構築するか?

  • ドメインロジックに焦点を合わせる
  • OOPでモジュール化する
  • インクリメンタルに設計する
    • 上記のことを前提すると、アップフロントに綺麗に設計はできない
    • ドメインロジックをコードに落としてモジュール化の実験をしながら徐々に完成させていく

従来のソフトウェアとの関心事の違い

  • DDD
    • ビジネスロジックに焦点を合わせる
    • 主な関心事はドメインの状況を表す値の種類, 値を使った計算、判定ロジック, 計算結果、判定結果の表現方法
  • 従来の設計
    • 入出力
    • 主な関心事はUI, DB, 通信など

ゲーム分野におけるドメインロジックの構成要素

  • ゲームを構成する概念(世界観、キャラクター設定、ストーリーに登場する言葉, etc)
  • ゲームの状況を表現する値(金, HP, MP, Exp, SP, アイテム種類, 武器種類, 防具種類, etc)
  • 戦闘開始状況の判定ルール, ダメージなどの計算ルール, 勝敗の判定ルール

これらを1つ1つの機能として整理してモジュール化する。

ドメインロジック以外の関心事

ビジュアルデザイン, SE, インタラクションデザインなど
これらはドメインロジックに従属する要素である。
DDDで設計されたソフトウェアはドメインロジックのまわりにこれらがぶら下がる感じで出来上がる。

ドメインロジックに焦点を合わせる理由

  1. ソフトウェアの複雑さの原因はドメインロジック(計算、判定ロジック)にある
  2. ドメインロジックと入出力が混在しているとドメインロジックの全体像や構造が見えてこない
  3. 入力の関心事を分離してドメインロジックだけを対象にするとドメインロジックの輪郭や構造がはっきりしてくる
  4. ドメインロジックの輪郭や構造がみえてくると計算、判定ロジックがはっきりしてくる
  5. ロジックの整理がしやすくなると入出力もシンプルになる
  6. この実感を感じられるようになるとDDDの恩恵がわかるようになる(入出力がシンプルになるから)
  7. 結果的にソフトウェア全体の見通しが良くなる
  8. どこに何があるかわかる
  9. コード変更による影響範囲がわかる

MVCモデルのソフトウェアのモデル部分をビジネスロジックと入出力処理に分離するイメージといえそう。

ゲーム開発への活用の可能性

ゲーム開発でのメリット

  • 計算ロジック、判定ロジックの記述が整理できる
    • 同一ロジックの重複記述の防止
    • ロジックの組み合わせ方の見通しをよくできる
  • 世界観、キャラクター設定、シナリオ構成と計算ロジック、判定ロジックの記述を直接的に関係づける
    • 全体が整合する
      • 逆に言いうと整合が取れていないとビジネスロジックを考えなおしてコンセプト(=世界観)などに合わせていくべき
    • 変更の対象箇所、影響範囲がわかりやすい
  • 入出力処理の記述がシンプル
    • 計算や判定の記述をドメイン層に外だし出来る
    • 一番実感しやすい

OOPでモジュール化するとは?

  • ソフトウェアのモジュール
    • コードを扱いやすい塊にグループ化して、グループごとに別ファイルで記述する
    • モジュール間の依存関係を少なくする
  • モジュール化のアプローチ
    • 入出力の単位ごと
    • 計算する値の種類ごと

入出力単位でモジュール化する方法

  • 入力を起点に、出力するための処理手順の単位でファイルを分ける
  • 機能分割し機能ごとに入力と出力を定義する
  • 出力に必要な一連の処理を時系列に記述する(手続き)
    • (Stringクラス の length と substr するのに時間は関係ないよね)

計算する値の種類ごとにモジュール化する方法

  • 計算や判定に登場する値の種類を見つける
    • レベル, HP, MP ...
    • 武器種類 ...
    • ダメージの判定結果、勝敗判定結果
  • 計算する値の種類ごとに必要な計算(演算)を整理する
    • 一致、不一致の判定
    • 大小判定
    • 境界値判定
    • 四則演算
    • 値のリテラル表現への変換
    • リテラル表現から値への変換

値の種類は多くて計算の種類は少ない

OOPのモジュール化の考え方とやり方を学ぶには?

  • 値オブジェクト, 区分オブジェクト, コレクションオブジェクト
  • 現場で役立つシステム設計の原則(登壇者の著書), オブジェクト指向入門とか参考になるよって言っていた

インクリメンタルに開発するとは?

  • 最初から良い設計はでいない
    • 開発を始めるときはゲームに対する知識が貧弱
    • 最初に書いたコードはあとからもっと良い書き方が見つかる
    • 最初の要求はあいまいで時間とともに具体的、詳細になる
    • 要求は時間とともに変化する
      • フィードバックの反映
      • 周りの環境の変化
  • 複雑な構造に対して最初から格闘していてもつくれない
    • 基本的な値の種類を見つける(例えば、HPクラスだけ最初に作ってみるとか)
    • 値と値を組み合わせたロジックを置く場所(第3のクラスを見つける)
  • 定番の答えはない, 開発してるゲームに合わせて探す

例えばRPGのようなゲームを作っているとどんな値の種類が見つかるか?

  • バトル型
    • 進行中の戦闘状態を表す状態を集めた型
    • ターン型オブジェクトの履歴を持つ
    • 戦闘結果を判定するロジックを持つ
  • バトルコンテキスト型
    • 戦闘開始時の状態を表現する型
    • 戦闘やターンの計算、判定に必要な基本情報を表現した値の集合
  • ターン型
    • 戦闘のターンごとの状況を表す値を集めた型
    • 前のターンの結果を計算して次のターンを返す
    • 次のターンから戦闘結果を判定する
  • ポイント型
    • HP, MP, SPなどの共通の定義
  • アイテム型
    • 食料、薬、巻物などのアイテム共通の定義

これらはあくまで例えばの話で、例えばアイテムに関しては特に全アイテム共通のアイテム型なんか持つ必要がなくて、 食料、薬、巻物別々に定義や所持枠を作るという方法もある。

ゲーム開発に取り入れる時の障害

どんな障害が起こるか

  • そうはいっても実際にソフトウェアを開発していると入出力の処理を重視しがちになる
    • UI命, 通信が〜, DBが〜
  • フレームワーク
  • 開発プロセス
    • 例えば見た目をすぐに修正して欲しいということがあったとして、 ビューモデルだけ変えるか, ビジネスモデルも変えるか・・・みたいなことが起きる

障害に対するいくつかのアプローチ

  • 少数精鋭のチームで徹底してDDDで開発してみる
    • 学習するための実験プロジェクトをやってみるなど
    • 効果は非常に高いけど難しい
  • 従来のやり方に少しずつ
    • 数値の計算ロジックを独自型にwrapして計算を行う
      • HPの単純な足し算、引き算のロジックをHP型にする、など
    • 判定結果をboolean型で表現することをやめ、独自に定義したenum型などを使って判定を行う
    • コレクションの操作を独自型にwrapして行う

実際にコードを書いてその結果を確認しながら議論し、決めていくことが一番大事!

質問やその他メモ

  • DDDでビジネスロジックを書いていたら固有名詞のクラスとかが生まれてきて気持ち悪い
    • ビジネスロジックでの計算のために使われているならいいと思いますよ的な
    • (けっこう難しいお話をされていたので多分こんな感じのことを言っていたと思うけどよく覚えていない。。)
  • 企画の人が完全に要件を定義していない。どうしよう?
    • 企画はビジネスロジックを完全に把握しているわけではなく、大体1割程度しか把握していない(よくて3割)
    • うまく聞き出せれば2, 3割くらいは聞き出せる
    • あとは開発側でよしなにしてしまおう
      • ただしコミュニケーションが不要なわけではない
      • ビジネスロジックを定義していく中で明らかに相談、共有しておいたほうがよいことがあると思えば迷わず相談、共有しておこう
  • 集約がむずかしくてよくわからない
    • そもそも集約はあまり作る必要がない
    • 複数のドメインモデルの間で変更の同期が必要なものがある場合にのみ作る
    • 入出力のときに必要だから集約を作ろうとしていない?
      • ビジネスモデルはあくまでビジネスロジックを計算する役割しか持たないので、入出力のときに必要なものはインフラ層でよしなにしましょう
  • エヴァンス本での集約のトランザクション整合性とはDBのトランザクションの話をしているわけではない
    • 複数のドメインモデルの間で変更の同期が必要なもののこと
    • 例えば商品の値段が上がれば請求書の値段もあがるみたいな(これはちょっと不自然なのでたとえとして悪いけど)