# 機能設計書 68-グループデータ永続化

## 概要

本ドキュメントは、Etherpadのグループデータ永続化機能の設計を記載する。この機能は、パッドをグループ化して管理するためのグループ情報をueberDB2を介してデータベースに保存する。

### 本機能の処理概要

グループデータ永続化機能は、複数のパッドをまとめて管理するグループの情報を管理・保存する機能である。グループを使用することで、パッドのアクセス制御やセッション管理を一元的に行うことができる。

**業務上の目的・背景**：企業や教育機関などでEtherpadを利用する場合、関連するパッドをグループとしてまとめて管理する必要がある。グループ機能により、特定のグループに属するパッドへのアクセスをセッションで制御したり、グループ単位でパッドを管理したりすることが可能となる。

**機能の利用シーン**：
- 新しいプロジェクト用のグループを作成する時
- グループ内にパッドを作成する時
- グループに属するパッド一覧を取得する時
- プロジェクト終了時にグループを削除する時
- 外部システムとのマッピングでグループを管理する時

**主要な処理内容**：
1. グループの作成（createGroup）
2. 条件付きグループ作成（createGroupIfNotExistsFor）
3. グループ内パッドの作成（createGroupPad）
4. グループの削除（deleteGroup）
5. グループ内パッド一覧取得（listPads）
6. 全グループ一覧取得（listAllGroups）

**関連システム・外部連携**：
- ueberDB2（データベース抽象化層）
- パッド管理（グループパッドの作成・削除）
- セッション管理（グループとセッションの関連付け）

**権限による制御**：グループ操作はAPI経由でのみ可能。グループパッドへのアクセスはセッションで制御される。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | （直接的な画面関連なし） | API | REST API経由でのグループ管理 |

## 機能種別

データ永続化 / CRUD操作

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| groupID | string | Yes（多くの操作で） | グループの識別子 | g.で始まる文字列 |
| groupMapper | string | Yes（条件付き作成時） | 外部システムとの関連付けキー | 文字列型 |
| padName | string | Yes（パッド作成時） | グループ内のパッド名 | 有効なパッド名 |
| text | string | No | パッドの初期テキスト | - |
| authorId | string | No | 作成者の著者ID | a.で始まる文字列 |

### 入力データソース

- REST API（グループ管理API）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| group:{groupID} | object | グループ情報オブジェクト |
| groups | object | 全グループIDのマップ |
| mapper2group:{mapper} | string | マッパーからグループIDへのマッピング |

### 出力先

- ueberDB2を介したデータベース

## 処理フロー

### 処理シーケンス

```
1. グループ作成リクエスト
   └─ createGroup() 呼び出し
2. グループID生成
   └─ g. + ランダム16文字
3. グループレコード保存
   └─ group:{groupID} に保存
4. グローバルレコード更新
   └─ groups にグループID追加
5. マッパー使用時
   └─ mapper2group:{mapper} に保存
   └─ group:{groupID}.mappings に記録
```

### フローチャート

```mermaid
flowchart TD
    A[グループ作成要求] --> B{条件付き?}
    B -->|Yes| C[mapper2groupから検索]
    C --> D{グループ存在?}
    D -->|Yes| E[既存groupID返却]
    D -->|No| F[createGroup呼び出し]
    B -->|No| F
    F --> G[groupID生成]
    G --> H[group:{groupID}保存]
    H --> I[groups更新]
    I --> J{マッパーあり?}
    J -->|Yes| K[mapper2group保存]
    K --> L[mappings更新]
    J -->|No| M[groupID返却]
    L --> M
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-68-01 | グループID形式 | グループIDは"g."プレフィックス + 16文字のランダム文字列 | グループ作成時 |
| BR-68-02 | グループパッドID形式 | グループパッドIDは"groupID$padName"形式 | グループパッド作成時 |
| BR-68-03 | グループ削除の連鎖 | グループ削除時は関連するパッドとセッションも削除 | グループ削除時 |
| BR-68-04 | マッパーの一意性 | 同一マッパーで異なるグループは作成不可 | 条件付き作成時 |

### 計算ロジック

グループID生成:
```typescript
const groupID = `g.${randomString(16)}`;
```

グループパッドID生成:
```typescript
const padID = `${groupID}$${padName}`;
```

## データベース操作仕様

### 操作別データベース影響一覧

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| グループ作成 | group:{groupID} | INSERT | グループ情報の保存 |
| グローバル登録 | groups | UPDATE | グループIDの追加 |
| マッパー登録 | mapper2group:{mapper} | INSERT | マッパーマッピングの保存 |
| グループ削除 | group:{groupID} | DELETE | グループ情報の削除 |
| グローバル削除 | groups | UPDATE | グループIDの削除 |
| パッド追加 | group:{groupID} | UPDATE | padsへのパッドID追加 |

### テーブル別操作詳細

#### group:{groupID}

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | pads | {} | パッドIDのマップ（初期値空） |
| INSERT | mappings | {} | マッパーのマップ（初期値空） |
| UPDATE | pads.{padID} | 1 | パッド追加時 |
| UPDATE | mappings.{mapper} | 1 | マッパー追加時 |

#### groups

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| UPDATE | {groupID} | 1 or undefined | 追加時は1、削除時はundefined |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| apierror | グループ不存在 | groupIDが存在しない | エラーメッセージを返却 |
| apierror | パッド名重複 | 同名パッドが既に存在 | エラーメッセージを返却 |
| apierror | マッパー不正 | groupMapperが文字列でない | エラーメッセージを返却 |

### リトライ仕様

エラー時は自動リトライなし。

## トランザクション仕様

グループ削除時は関連するパッドとセッションも削除される。削除処理は並列実行され、ueberDB2のsetSub()によるアトミック更新で整合性を維持。

## パフォーマンス要件

- グループ作成: 即時完了
- グループ削除: 関連パッド・セッション数に依存

## セキュリティ考慮事項

- グループIDはランダム生成で推測困難
- グループパッドへのアクセスはセッションで制御

## 備考

- グループパッドはpublicStatusを設定可能
- グループ削除は関連データも含めて完全削除

---

## コードリーディングガイド

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | GroupManager.ts | `src/node/db/GroupManager.ts` | グループ情報の構造 |

**読解のコツ**: グループは{pads, mappings}の2つの属性を持つ。padsはパッドIDのマップ、mappingsはマッパーのマップ。

#### Step 2: グループ作成処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | GroupManager.ts | `src/node/db/GroupManager.ts` | createGroup関数（96-104行目） |
| 2-2 | GroupManager.ts | `src/node/db/GroupManager.ts` | createGroupIfNotExistsFor関数（111-127行目） |

**主要処理フロー**:
1. **96-104行目**: createGroup関数 - グループ作成
2. **97行目**: グループID生成
3. **98行目**: group:{groupID}の保存
4. **102行目**: groupsへの追加
5. **111-127行目**: createGroupIfNotExistsFor関数 - 条件付き作成

#### Step 3: グループパッド作成処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | GroupManager.ts | `src/node/db/GroupManager.ts` | createGroupPad関数（137-163行目） |

**主要処理フロー**:
- **139行目**: パッドID生成（groupID$padName）
- **142-146行目**: グループ存在確認
- **148-153行目**: パッド存在確認
- **156行目**: padManager.getPadでパッド作成
- **159行目**: グループのpadsに追加

#### Step 4: グループ削除処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | GroupManager.ts | `src/node/db/GroupManager.ts` | deleteGroup関数（45-78行目） |

**主要処理フロー**:
- **46-52行目**: グループ存在確認
- **54-58行目**: 関連パッドの削除
- **61-65行目**: 関連セッションの削除
- **67-74行目**: マッパー・グループレコードの削除
- **77行目**: group:{groupID}の削除

### プログラム呼び出し階層図

```
REST API（グループ作成）
    │
    └─ groupManager.createGroup()
           │
           ├─ randomString(16) → groupID生成
           │
           ├─ db.set(`group:${groupID}`, {pads: {}, mappings: {}})
           │
           └─ db.setSub('groups', [groupID], 1)

REST API（グループ削除）
    │
    └─ groupManager.deleteGroup(groupID)
           │
           ├─ db.get(`group:${groupID}`)
           │
           ├─ padManager.getPad(padId) → pad.remove() × N
           │
           ├─ sessionManager.deleteSession(sessionId) × N
           │
           ├─ db.remove(`mapper2group:${mapper}`) × N
           │
           ├─ db.setSub('groups', [groupID], undefined)
           │
           └─ db.remove(`group:${groupID}`)
```

### データフロー図

```
[入力]               [処理]                    [出力]

APIリクエスト ───▶ createGroup()
                         │
                         ▼
                 groupID生成
                         │
         ┌───────────────┼───────────────┐
         │               │               │
         ▼               ▼               ▼
  group保存        groups更新     mapper2group
         │               │               │
         └───────────────┼───────────────┘
                         │
                         ▼
                  groupID返却

APIリクエスト ───▶ deleteGroup()
                         │
         ┌───────────────┼───────────────┐
         │               │               │
         ▼               ▼               ▼
  パッド削除    セッション削除   マッパー削除
         │               │               │
         └───────────────┼───────────────┘
                         │
                         ▼
                  グループ削除
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| GroupManager.ts | `src/node/db/GroupManager.ts` | ソース | グループ管理本体 |
| DB.ts | `src/node/db/DB.ts` | ソース | DB抽象化 |
| PadManager.ts | `src/node/db/PadManager.ts` | ソース | パッド管理連携 |
| SessionManager.ts | `src/node/db/SessionManager.ts` | ソース | セッション管理連携 |
| Pad.ts | `src/node/db/Pad.ts` | ソース | パッド削除処理 |
| API.ts | `src/node/db/API.ts` | ソース | API層 |
