【DDD/UCDD】Unityで意識している設計手法【オニオンアーキテクチャ】

Unity

はじめに

筆者が個人でUnity開発を行う上で、どのような規模のプロダクトにおいても重要視しているのが「設計」です。

初学者のころは、レイヤー分けやクラス設計など考慮せずに MonoBehaviour ベタ書きで作っていました。
設計に重きを置かないため、成果物が動作するまでのスピード感は良い点でしたが、進むにつれて整合性が取れなくなったり、機能の拡張がしにくいなどの悪い点が目立ちました。
結果、思い描いた品質を担保できなかったり、開発のモチベーションが低下するなど、苦い思いをすることが多々ありました。

これらの経験を踏まえて、開発体験と成果物の品質をより良いものにしたい!というモチベーションで設計工程に注力するようになりました。

本記事では、私が取り入れている設計手法とディレクトリ構成、メリット・デメリットをまとめます。
これからUnityプロダクトに設計を取り入れたい方や、同じ思いをして悩んだことのある方に届けば幸いです。

設計手法

「UCDD (UseCase Driven Design: ユースケース駆動設計) 「軽量DDD (Domain Driven Design: ドメイン駆動設計)」の手法を採用しています。

UCDD (ユースケース駆動設計): システムのユースケース(機能)に焦点を当てるソフトウェア設計手法。

DDD (ドメイン駆動設計): システムのドメイン(ビジネス領域)の要件を満たすよう、ドメインのモデリングに焦点を当てるソフトウェア設計手法。

軽量DDD(戦術的DDD)とは、整理したドメインをソフトウェアで実現するためのレイヤー分けや実装パターンを指す。

はじめに、UCDDにおける「ICONIXプロセス」を用い、ユースケースをもとに要件を整理します。
その後、軽量DDDの方針で、ドメインをもとに設計を行っていきます。

流れは以下の通りです。

1. UCDD

  1. ドメインモデリング
    1. 要求整理
    2. 用語整理
    3. ドメインモデル図の作成
  2. ユースケースモデリング
    1. ユースケース図の作成
    2. ユースケース記述の作成
  3. ロバストネス分析
    1. ロバストネス図の作成
    2. ドメインモデルの更新

2. DDD

  1. 集約設計

各項目の詳細はこちらの記事が非常に参考になります。

レイヤー構造

以下の4層から成る「オニオンアーキテクチャ」を採用しています。

  • ドメイン層 (Domain Layer)
  • アプリケーション層 (Application Layer)
  • プレゼンテーション層 (Presentation Layer)
  • インフラストラクチャ層 (Infrastructure Layer)

使用ライブラリ

前提条件として、以下のライブラリを使用します。

  • R3
    • クラス/レイヤー間のイベント駆動処理実装
  • VContainer
    • クラスの依存性注入 (DI: Dependency Injection)の実装

それぞれの導入方法については、以下の記事を参照ください。

ディレクトリ構成

ディレクトリは軽量DDDを参考に以下のレイヤー分けとします。

Assets/
└── Scripts/
    ├── Application/   # アプリケーション層
    │   ├── Common/      # 定数・共通処理
    │   ├── DTOs/        # 非ドメイン知識のデータクラス
    │   ├── Services/    # 外部機能サービスのインタフェース
    │   └── UseCases/    # ユースケースクラス
    │
    ├── Core/                         # レイヤー外の共通部品
    │   ├── DI/                         # DI
    │   │   ├── XxxSceneLifetimeScope.cs  # XXX画面のDIコンテナ
    │   │   └── RootLifetimeScope.cs      # 共通DIコンテナ
    │   ├── Extension/                  # 拡張
    │   └── Utils/                      # 共通ユーティリティ
    │
    ├── Domain/         # ドメイン層
    │   ├── Common/       # 定数・共通処理・基底クラス
    │   ├── Factories/    # ファクトリ
    │   ├── Models/       # ドメインモデル(集約単位)
    │   │   └── ...
    │   ├── Repositories/ # リポジトリのインタフェース
    │   └── Services/     # ドメインサービス
    │
    ├── Infrastructure/ # インフラストラクチャ層
    │   ├── Repositories/ # リポジトリ
    │   │   └── ...
    │   └── Services/     # 外部機能サービス
    │       ├── Audio/      # 音響(BGM・SE)
    │       ├── Input/      # 入力処理
    │       ├── Scene/      # シーン遷移
    │       └── Time/       # 時間管理
    │
    └── Presentation/   # プレゼンテーション層
        ├── Common/       # 定数・共通処理・基底クラス
        ├── Objects/      # ゲームオブジェクト
        │   └── ...         # 配下にView/Presenter
        ├── Scenes/       # シーン管理
        └── UIs/          # UI
            └── ...         # 配下にView/Presenter

各レイヤーが持つオブジェクトとその関係を以下の図に示します。

classDiagram
direction TB
    Factories <-- Repositories
    Models <-- Factories
    Models --* Repositories
    Models <-- Services
    Services <-- UseCases

    Repositories --o UseCases
    DTOs <-- Services_

    Repositories <|.. Repositories_
    Services_ <|.. Services__

    Services_ <-- Objects
    Services_ <-- UIs   
    UseCases <-- Objects
    UseCases <-- UIs

    Objects --* Scenes
    UIs --* Scenes

    namespace Domain {
        class Common["Common"]
        class Factories
        class Models
        class Repositories
        class Services["Services"]
    }

    namespace Application {
        class Common_["Common"]
        class DTOs
        class Services_["Services"]
        class UseCases
    }

    namespace Infrastrucutre {
        class Repositories_["Repositories"]
        class Services__["Services"]
    }

    namespace Presentation {
        class Common__["Common"]
        class Objects
        class Scenes
        class UIs
    }

ドメイン層 (Domain Layer)

  • アプリケーションのドメイン(エンティティ、値オブジェクトなど)を実装する。

アプリケーション層 (Application Layer)

  • アプリケーションが持つ機能(ビジネスロジック)を実装する。

プレゼンテーション層 (Presentation Layer)

  • UIやゲーム内のオブジェクトの表示・イベントハンドリングを実装する。

インフラストラクチャ層 (Infrastructure Layer)

  • Unityエンジンの機能や外部データソースとの連携、データの永続化機能を実装する。

メリット

  • クラスをレイヤー分けすることで、クラスの責務やクラス間の関係を綺麗にできる。
  • クラスが疎結合になるため、機能を追加する際、既存機能のロジックへの影響を最小限にできる。
  • 作業期間が空いても、上記のようなルールに則っていればキャッチアップまで時間がかからない。

デメリット

  • 学習コスト、設計工数がかかる。
  • 実装を開始しても、動作確認まで時間がかかる。
  • 抽象化(抽象クラス、インタフェース)を多用するため、ファイル数が肥大化する。

まとめ

筆者がUnity開発を行うときに用いる設計手法とディレクトリ構成、またそのメリット・デメリットをまとめました。

取り入れるまでのコストはある程度かかるものの、コストに見合うかそれ以上の恩恵を得ることができると感じています。
ディレクトリ構成だけなど部分的にでも参考になれば幸いです。

今回は概要的なまとめでしたが、今後具体例も交えて紹介できればと思います。
ご覧いただきありがとうございました。

タイトルとURLをコピーしました