Open6

UnityとFirebaseでアバターシステムを実装

0y00y0

目標

Unityでユーザーが以下を自由に編集できるシステムを実装

  • 体型の変更(身長・肩幅など)

現状の成果物

ボーンスケーリングにより体型変更が可能(セグメントスケール補正は未実装)
UIはUIツールキット

0y00y0

サーバ設計

Firebaseで認証、API、DBを実装

プロジェクト構造 (functions/src)

functions/src/
├── application/         # アプリケーションロジック、ユースケース、サービス
│   ├── errors/          # カスタムエラータイプ
│   ├── services/        # ユースケースを調整するアプリケーションサービス
│   ├── validators/      # リクエストバリデーター
│   └── utils/           # ユーティリティ関数 (型ガードなど)
├── config/              # 設定値、定数
├── domain/              # コアドメインロジックとモデル
│   └── avatarSettings.ts # アバター設定のドメインエンティティ/モデル (Height, SkinColor など)
├── infrastructure/      # 外部関心事 (DB、フレームワーク)
│   ├── database/        # データアクセスロジック (Firestore など)
│   └── types/           # インフラストラクチャ固有の型 (Firestore データマッピングなど)
└── presentation/        # API エンドポイント、リクエスト処理、DTO
    ├── controllers/     # HTTP をアプリケーションサービスにマッピングするリクエストハンドラ
    ├── middleware/      # Express ミドルウェア (認証、検証など)
    └── types/           # プレゼンテーション層固有の型 (API レスポンス、Express 拡張)

レイヤー

  1. ドメイン層
    • コアビジネスロジックとエンティティ (例: AvatarSettings クラス - Height, SkinColor 等を定義)
    • 他のレイヤーへの依存関係は無い
  2. アプリケーション層
    • ドメインオブジェクトとインフラストラクチャサービスを調整してユースケースを編成
    • リクエストバリデーターもここに配置
  3. インフラストラクチャ層
    • データベース、外部 API など、外部の関心事についてアプリケーション層によって定義されたインターフェースを実装ドメイン層とアプリケーション層に依存
  4. プレゼンテーション層
    • 外部とのやり取り (HTTP リクエスト) を処理
    • コントローラー、ミドルウェア、API 固有の型 (DTO、レスポンス形式)、フレームワーク固有の拡張機能が含まれる
    • アプリケーション層に依存
  5. 設定層
    • アプリケーション全体で使用する定数と設定を含む
    • 必要に応じて他のレイヤーからアクセス可能
0y00y0

API エンドポイント

GET /api/v1/status

認証済みユーザー向けのAPIステータス確認エンドポイント
主にクライアントがAPIサーバーとの疎通確認や認証状態を確認するために使用

認証: Authorization: Bearer <token> ヘッダーに有効な Firebase Auth ID トークンが必要

レスポンス:

  • 200 OK (成功): (index.ts の実装に基づく)
    {
      "action": "status",
      "message": "認証OK",
      "timestamp": "2024-08-15T12:00:00.000Z", // ISO 8601 形式のタイムスタンプ
      "userId": "user-firebase-uid"
    }
    

/api/v1/avatar-settings

認証されたユーザーのアバター設定を管理するエンドポイント群

認証: すべてのメソッドで Authorization: Bearer <token> ヘッダーに有効な Firebase Auth ID トークンが必要


GET /api/v1/avatar-settings

認証されたユーザーの現在のアバター設定を取得

レスポンス:

  • 200 OK (成功): (avatarSettingsController.ts および domain/avatarSettings.tstoJSON に基づく)

    {
      "action": "read",
      "id": "user-firebase-uid", // ユーザーID (ドメインモデルの id プロパティ)
      "data": {
        "id": "user-firebase-uid",
        "Height": 1.8,
        "ShoulderWidth": 1.1,
        "BodyWidth": 1.0,
        "HeadSize": 1.2,
        "SkinColorR": 0.8, "SkinColorG": 0.6, "SkinColorB": 0.4, "SkinColorA": 1.0,
        "HairColorR": 0.1, "HairColorG": 0.1, "HairColorB": 0.1, "HairColorA": 1.0,
        "createdAt": "2024-08-15T10:00:00.000Z", // ISO 8601 形式
        "updatedAt": "2024-08-15T11:00:00.000Z" // ISO 8601 形式
      }
    }
    

    注意: data オブジェクトは、AvatarSettings ドメインモデルの toJSON() メソッドによってシリアライズされたもの。タイムスタンプは ISO 8601 文字列として表現

  • 401 Unauthorized: 認証トークンが無効な場合など

  • 404 Not Found: 設定が見つからない場合

  • 500 Internal Server Error: 予期しないサーバーエラーの場合


POST /api/v1/avatar-settings

認証されたユーザーのアバター設定を新規に作成

リクエストボディ:

{
  "Height": 1.8,
  "ShoulderWidth": 1.1,
  "BodyWidth": 1.0,
  "HeadSize": 1.2,
  "SkinColorR": 0.8, "SkinColorG": 0.6, "SkinColorB": 0.4, "SkinColorA": 1.0,
  "HairColorR": 0.1, "HairColorG": 0.1, "HairColorB": 0.1, "HairColorA": 1.0
}
  • 必須フィールド: Height, ShoulderWidth, BodyWidth, HeadSize, SkinColorR/G/B/A, HairColorR/G/B/A
  • バリデーションルール:
    • Height: 0.5 - 2.0
    • ShoulderWidth, BodyWidth: 0.5 - 1.5
    • HeadSize: 0.5 - 1.5
    • 各カラー成分 (*ColorR/G/B/A): 0.0 - 1.0
    • すべての値は数値

レスポンス:

  • 201 Created (成功):

    {
      "action": "create",
      "id": "user-firebase-uid",
      "data": {
        "id": "user-firebase-uid",
        "Height": 1.8,
        "ShoulderWidth": 1.1,
        "BodyWidth": 1.0,
        "HeadSize": 1.2,
        "SkinColorR": 0.8, "SkinColorG": 0.6, "SkinColorB": 0.4, "SkinColorA": 1.0,
        "HairColorR": 0.1, "HairColorG": 0.1, "HairColorB": 0.1, "HairColorA": 1.0,
        "createdAt": "2024-08-15T11:30:00.000Z", // 作成時のタイムスタンプ (ISO 8601)
        "updatedAt": "2024-08-15T11:30:00.000Z" // 作成時のタイムスタンプ (ISO 8601)
      }
    }
    
  • 400 Bad Request (バリデーションエラー):

    {
      "error": "リクエストのバリデーションに失敗しました",
      "code": "VALIDATION_ERROR",
      "timestamp": "...",
      "validationErrors": [
        { "type": "field", "msg": "Height は 0.5 から 2 の範囲内である必要があります。", "path": "Height", "location": "body", "value": 3.0 },
        // ... 他の検証エラー (例: 型エラー、範囲外、必須フィールド欠落)
      ]
    }
    
  • 401 Unauthorized: 認証トークンが無効な場合など。

  • 500 Internal Server Error: 予期しないサーバーエラーの場合。


PUT /api/v1/avatar-settings

認証されたユーザーの既存のアバター設定を更新
リクエストボディに含まれるフィールドのみが更新対象(部分更新)

リクエストボディ:

{
  "Height": 1.9, // 更新したいフィールドを含める
  "SkinColorR": 0.9
  // 他のフィールドは省略可能
}
  • 更新したいフィールドとその値を含める
  • バリデーションルールは POST と同様にリクエストに含まれるフィールドに対して適用

レスポンス:

  • 200 OK (成功):

    {
      "action": "update",
      "id": "user-firebase-uid",
      "data": {
        "id": "user-firebase-uid",
        "Height": 1.9, // 更新された値
        "ShoulderWidth": 1.1, // 既存の値
        "BodyWidth": 1.0, // 既存の値
        "HeadSize": 1.2, // 既存の値
        "SkinColorR": 0.9, // 更新された値
        "SkinColorG": 0.6, // 既存の値
        "SkinColorB": 0.4, // 既存の値
        "SkinColorA": 1.0, // 既存の値
        "HairColorR": 0.1, // 既存の値
        "HairColorG": 0.1, // 既存の値
        "HairColorB": 0.1, // 既存の値
        "HairColorA": 1.0, // 既存の値
        "createdAt": "2024-08-15T10:00:00.000Z", // 既存の値 (ISO 8601)
        "updatedAt": "2024-08-15T11:45:00.000Z" // 更新時のタイムスタンプ (ISO 8601)
      }
    }
    
  • 400 Bad Request (バリデーションエラー): リクエストボディのバリデーションに失敗した場合。(エラー形式は POST と同様)

  • 401 Unauthorized: 認証トークンが無効な場合など

  • 404 Not Found: 更新対象の設定が見つからない場合

  • 500 Internal Server Error: 予期しないサーバーエラーの場合


DELETE /api/v1/avatar-settings

認証されたユーザーのアバター設定を削除

レスポンス:

  • 200 OK (成功): (avatarSettingsController.ts の実装に基づく)

    {
      "action": "delete",
      "id": "user-firebase-uid",
      "message": "アバター設定が正常に削除されました"
    }
    
  • 401 Unauthorized: 認証トークンが無効な場合など

  • 404 Not Found: 削除対象の設定が見つからない場合

  • 500 Internal Server Error: 予期しないサーバーエラーの場合