|
| 1 | +# Global Club ID Design |
| 2 | + |
| 3 | +## 概要 |
| 4 | + |
| 5 | +このドキュメントは、Raspberry Pi Foundation が管理する国際的な Clubs DB と CoderDojo Japan の Japan DB を連携させる機能の設計と実装について記載しています。 |
| 6 | + |
| 7 | +## 背景 |
| 8 | + |
| 9 | +### 現状 |
| 10 | + |
| 11 | +- Clubs DB(Global Clubs)と Japan DB(CoderDojo Japan)が独立している |
| 12 | +- 両者を紐付ける ID がない |
| 13 | + |
| 14 | +### 解決策 |
| 15 | +Clubs DB の ID(`global_club_id`)を Japan DB の Dojo と紐付ける。 |
| 16 | + |
| 17 | +## 設計決定 |
| 18 | + |
| 19 | +### 1. 命名規則 |
| 20 | + |
| 21 | +#### `global_club_id` を選択した理由 |
| 22 | +- `club_id` では `dojo_id` との違いが不明瞭 |
| 23 | +- `global_` プレフィックスにより、外部システムの ID であることが明確 |
| 24 | +- 将来的に状況が変わっても、名前を変更する必要がない |
| 25 | + - 想定ケース: CoderDojo Foundation (Zen API) -> Raspberry Pi Foundation (Clubs API) -> ??? (FooBar API) |
| 26 | + |
| 27 | +#### `GlobalClubs` モジュール名を選択した理由 |
| 28 | +- 現在は Raspberry Pi Foundation が管理しているが、過去には CoderDojo Foundation (Zen) が管理していた |
| 29 | +- 将来的に同じような変更が行われる可能性を考慮 (想定例: GraphQL の廃止、REST API への回帰、など) |
| 30 | +- 実装は現在の API(Raspberry Pi)に直接接続するが、命名は抽象的に保つ |
| 31 | + |
| 32 | +### 2. 実装方針 |
| 33 | + |
| 34 | +#### YAGNI 原則の適用 |
| 35 | +当初はアダプターパターンを検討したが、以下の理由でシンプルな実装を選択: |
| 36 | +- API プロバイダーの変更は頻繁には起こらない(もしくは永遠に起こらない) |
| 37 | +- 過度な抽象化は開発速度を低下させる |
| 38 | +- 必要になったときにリファクタリングすればよい |
| 39 | + |
| 40 | +#### API メソッドの命名 |
| 41 | +```ruby |
| 42 | +def fetch_global_clubs(organization_slug: 'coderdojo', after: nil) |
| 43 | +``` |
| 44 | +- `fetch_coderdojo_clubs` ではなく `fetch_global_clubs` を選択 |
| 45 | +- API は CoderDojo 以外の組織のクラブも返すため、より正確な命名 |
| 46 | +- `organization_slug` パラメータでフィルタリング可能 |
| 47 | + |
| 48 | +## 技術仕様 |
| 49 | + |
| 50 | +> **⚠️ 注意**: この節以降の内容は実装過程で変更される可能性があります。実際の実装とレビューを経て確定します。(2025-01-20時点) |
| 51 | +
|
| 52 | +### API エンドポイント |
| 53 | +- GraphQL: `https://clubs-api.raspberrypi.org/graphql` |
| 54 | +- 認証: 不要(読み取り専用) |
| 55 | +- レート制限: 60 req/min |
| 56 | + |
| 57 | +### データモデル |
| 58 | + |
| 59 | +#### Dojo モデルの拡張 |
| 60 | +```ruby |
| 61 | +# マイグレーション(必要なのはこれだけ!) |
| 62 | +# UUID 文字列(例: "18704b53-1042-4464-9d49-8820c6ff8c97") |
| 63 | +add_column :dojos, :global_club_id, :string, null: false |
| 64 | +add_index :dojos, :global_club_id, unique: true |
| 65 | +``` |
| 66 | + |
| 67 | +### マッピング戦略 |
| 68 | + |
| 69 | +#### 初期マッピング(一度だけ実行) |
| 70 | +1. Clubs DB から全 CoderDojo を取得 |
| 71 | +2. 既存の Dojo と手動で照合: |
| 72 | + - 名前は異なることが前提(Clubs DB は英語、Japan DB は日本語) |
| 73 | + - Japan DB には位置情報がないため、地理的照合は不可 |
| 74 | +3. 人間が判断して `global_club_id` を設定 |
| 75 | + |
| 76 | +**重要な仕様理解**: |
| 77 | +- **名前の違いは正常**: Clubs DB(英語話者向け)と Japan DB(日本語話者向け)で名前が異なるのは仕様 |
| 78 | +- **位置情報**: Japan DB にはなく、Clubs DB のみに存在 |
| 79 | +- **用途例**: DojoMap アプリで global_club_id を使って英語名→日本語名の変換を行う |
| 80 | + |
| 81 | +## 実装タスク(Issue #1616 より) |
| 82 | + |
| 83 | +1. **Dojo モデルに `global_club_id` カラムを追加** |
| 84 | + ```ruby |
| 85 | + class AddGlobalClubIdToDojos < ActiveRecord::Migration[8.0] |
| 86 | + def change |
| 87 | + # UUID 文字列(例: "18704b53-1042-4464-9d49-8820c6ff8c97") |
| 88 | + add_column :dojos, :global_club_id, :string, null: false |
| 89 | + add_index :dojos, :global_club_id, unique: true |
| 90 | + end |
| 91 | + end |
| 92 | + ``` |
| 93 | + |
| 94 | +2. **db/dojos.yml の全 Dojo に `global_club_id` を追加** |
| 95 | + |
| 96 | +3. **Clubs DB と Dojo ID を紐付ける仕組みを作成** |
| 97 | + |
| 98 | + |
| 99 | + |
| 100 | + |
| 101 | + |
| 102 | +## セキュリティ考慮事項 |
| 103 | + |
| 104 | +- HTTPS 通信(読み取り専用 API) |
| 105 | + |
| 106 | +## 運用上の注意点 |
| 107 | + |
| 108 | +### データの整合性 |
| 109 | +- **ユニーク制約**: 同じ `global_club_id` を持つ複数の Dojo は作成できない |
| 110 | +- **NOT NULL 制約**: すべての Dojo に `global_club_id` が必須 |
| 111 | +- **修正方法**: db/dojos.yml を編集して DB に反映(通常の運用通り) |
| 112 | + |
| 113 | + |
| 114 | +## 成功指標 |
| 115 | + |
| 116 | +1. **global_club_id カラムの追加完了** |
| 117 | +2. **既存 Dojo への ID 設定完了** |
| 118 | +3. **DojoMap への自動反映の実現**(Issue #1616 の目的) |
| 119 | + |
| 120 | +## シンプルなワークフロー |
| 121 | + |
| 122 | +### 新規 Dojo 追加時(申請フォームで対応) |
| 123 | + |
| 124 | +``` |
| 125 | +1. 申請時に Clubs DB での登録状況を確認してもらう |
| 126 | +2. global_club_id を申請フォームに記入(必須) |
| 127 | +3. db/dojos.yml に global_club_id 付きで追加 |
| 128 | +``` |
| 129 | + |
| 130 | +### 初期マッピング(一度だけ実行するスクリプト) |
| 131 | + |
| 132 | +```ruby |
| 133 | +# scripts/map_global_clubs.rb |
| 134 | +# 既存の全 Dojo に global_club_id を設定 |
| 135 | + |
| 136 | +clubs = GlobalClubs.fetch_all_coderdojo_clubs # 読み取り専用 API |
| 137 | +dojos = Dojo.active |
| 138 | + |
| 139 | +puts "Clubs DB: #{clubs.count} clubs" |
| 140 | +puts "Japan DB: #{dojos.count} dojos" |
| 141 | +puts "" |
| 142 | +puts "Manual mapping needed:" |
| 143 | +puts "Clubs DB (English) | Japan DB (Japanese)" |
| 144 | +puts "---------------------------|--------------------" |
| 145 | + |
| 146 | +clubs.each do |club| |
| 147 | + # club.id は UUID 文字列(例: "18704b53-1042-4464-9d49-8820c6ff8c97") |
| 148 | + puts "#{club.name.ljust(26)} | ???" |
| 149 | +end |
| 150 | + |
| 151 | +# 手動で db/dojos.yml に global_club_id を追加 |
| 152 | +# 例: global_club_id: "18704b53-1042-4464-9d49-8820c6ff8c97" |
| 153 | +``` |
| 154 | + |
| 155 | + |
| 156 | + |
| 157 | +## 更新履歴 |
| 158 | + |
| 159 | +- 2025-01-20: 初版作成(設計決定まで確定、技術仕様以降は暫定版) |
| 160 | +- 2025-08-18: YAGNI原則に基づく大幅簡素化 |
| 161 | + - 不要なカラム削除(confidence, last_sync) |
| 162 | + - 継続的同期を削除(一度きりのマッピングに変更) |
| 163 | + - 複雑な監視・測定機能を削除 |
| 164 | + - シンプルなワークフローに変更 |
| 165 | + - CoderDojo運用の実態に合わせた現実的な設計に修正 |
| 166 | + |
| 167 | +## 実装チェックリスト |
| 168 | + |
| 169 | +### 必要最小限の実装 |
| 170 | +- [ ] `global_club_id` カラムの追加(string型、NOT NULL、UUID) |
| 171 | +- [ ] 読み取り専用の Clubs API クライアント実装 |
| 172 | +- [ ] 既存 Dojo への一括マッピングスクリプト |
| 173 | +- [ ] 申請フォームに global_club_id 入力欄追加(必須) |
| 174 | + |
| 175 | +### やらないこと(YAGNI) |
| 176 | +- ❌ APIトークン管理(読み取り専用なので不要) |
| 177 | +- ❌ 継続的な同期処理 |
| 178 | +- ❌ null を許可(全 Dojo に必須) |
| 179 | +- ❌ 複雑な監視ダッシュボード |
| 180 | +- ❌ 自動マッチング(手動で確実に設定) |
0 commit comments