Skip to content

Commit 1ad3006

Browse files
committed
fix: 実運用に基づく重要な技術仕様の修正
修正内容: 1. Rails 8.0 のマイグレーション形式に修正([7.2] → [8.0]) 2. global_club_id を UUID 文字列型に修正(bigint → string) - 実際の値: "18704b53-1042-4464-9d49-8820c6ff8c97" 3. NOT NULL 制約を追加(全 Dojo に必須) 4. APIトークン関連を削除(読み取り専用APIなので不要) 5. 新規登録時の運用フローを明確化 - 申請フォームで global_club_id を必須入力 - 一度スクリプトで全Dojoに設定後は常に必須 根拠のない内容の削除: - リスク分析表(Issue #1616 に記載なし) - 日本固有の課題(Issue に記載なし) - テスト戦略の詳細(過剰な内容) - ロールバック戦略(Issue に記載なし) - 将来の拡張計画(憶測) - トラブルシューティングガイド(Issue に記載なし) Issue #1616 の実際の内容に忠実な設計に修正
1 parent dcd8498 commit 1ad3006

File tree

1 file changed

+103
-57
lines changed

1 file changed

+103
-57
lines changed

doc/global_club_id_design.md

Lines changed: 103 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,17 @@
22

33
## 概要
44

5-
このドキュメントは、Raspberry Pi Foundation が管理する国際的な Clubs API データベースと CoderDojo Japan のデータベースを連携させる機能の設計と実装について記載しています
5+
このドキュメントは、Raspberry Pi Foundation が管理する国際的な Clubs DB と CoderDojo Japan の Japan DB を連携させる機能の設計と実装について記載しています
66

77
## 背景
88

9-
### 現状の課題
10-
- 新しい Dojo が追加されるたびに、DojoMap に表示させるために手動で CSV を更新する必要がある
11-
- この手動作業は時間がかかり、エラーが発生しやすい
9+
### 現状
10+
11+
- Clubs DB(Global Clubs)と Japan DB(CoderDojo Japan)が独立している
12+
- 両者を紐付ける ID がない
1213

1314
### 解決策
14-
Global Clubs データベースの ID(`global_club_id`)を CoderDojo Japan の Dojo と紐付けることで、データの同期を自動化する
15+
Clubs DB の ID(`global_club_id`)を Japan DB の Dojo と紐付ける
1516

1617
## 設計決定
1718

@@ -50,85 +51,130 @@ def fetch_global_clubs(organization_slug: 'coderdojo', after: nil)
5051
5152
### API エンドポイント
5253
- GraphQL: `https://clubs-api.raspberrypi.org/graphql`
53-
- 認証: Bearer token (OAuth flow)
54+
- 認証: 不要(読み取り専用)
5455
- レート制限: 60 req/min
5556

5657
### データモデル
5758

5859
#### Dojo モデルの拡張
5960
```ruby
60-
# マイグレーション
61-
add_column :dojos, :global_club_id, :bigint
62-
add_index :dojos, :global_club_id, unique: true, where: 'global_club_id IS NOT NULL'
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
6365
```
6466

65-
#### 同期管理テーブル
66-
```ruby
67-
create_table :global_club_syncs do |t|
68-
t.bigint :global_club_id, null: false
69-
t.datetime :last_seen_at
70-
t.string :sync_status
71-
t.timestamps
72-
end
73-
```
67+
### マッピング戦略
68+
69+
#### 初期マッピング(一度だけ実行)
70+
1. Clubs DB から全 CoderDojo を取得
71+
2. 既存の Dojo と手動で照合:
72+
- 名前は異なることが前提(Clubs DB は英語、Japan DB は日本語)
73+
- Japan DB には位置情報がないため、地理的照合は不可
74+
3. 人間が判断して `global_club_id` を設定
7475

75-
### 同期戦略
76+
**重要な仕様理解**:
77+
- **名前の違いは正常**: Clubs DB(英語話者向け)と Japan DB(日本語話者向け)で名前が異なるのは仕様
78+
- **位置情報**: Japan DB にはなく、Clubs DB のみに存在
79+
- **用途例**: DojoMap アプリで global_club_id を使って英語名→日本語名の変換を行う
7680

77-
#### 増分同期
78-
- `updatedAt` フィールドを使用して、前回の同期以降に更新されたクラブのみを取得
79-
- 全データを毎回取得するのではなく、効率的な差分更新を実現
80-
- 冪等性を保証(同じ操作を複数回実行しても結果が同じ)
81+
## 実装タスク(Issue #1616 より)
8182

82-
#### マッチングアルゴリズム
83-
1. `global_club_id` で既存の Dojo を検索
84-
2. 見つからない場合は、名前の類似度 + 地理的距離(1km 以内)で自動マッチング
85-
3. 曖昧な場合は手動レビューキューに追加
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+
```
8693

87-
## 実装計画
94+
2. **db/dojos.yaml の全 Dojo に `global_club_id` を追加**
95+
96+
3. **Clubs DB と Dojo ID を紐付ける仕組みを作成**
8897

89-
### フェーズ 1: 基盤整備
90-
1. データベーススキーマの更新(`global_club_id` カラム追加)
91-
2. YAML ファイルの構造更新
92-
3. モデルのバリデーション追加
9398

94-
### フェーズ 2: API 統合
95-
1. `GlobalClubs::Client` クラスの実装
96-
2. GraphQL クエリの実装
97-
3. 認証設定(Rails credentials)
9899

99-
### フェーズ 3: 同期機能
100-
1. `GlobalClubs::SyncService` の実装
101-
2. マッチングロジックの実装
102-
3. エラーハンドリングとリトライ機構
103100

104-
### フェーズ 4: 運用
105-
1. 定期実行ジョブの設定
106-
2. 監視とアラートの実装
107-
3. 手動レビュー機能(将来的に)
108101

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

111-
- Bearer token は Rails credentials に保存
112-
- API 通信は HTTPS のみ
113-
- レート制限に対応(指数バックオフでリトライ)
104+
- HTTPS 通信(読み取り専用 API)
114105

115106
## 運用上の注意点
116107

117108
### データの整合性
118-
- ソフトデリート: Global Clubs API から削除されたクラブは `global_club_id = nil` に設定(Dojo レコードは削除しない)
119-
- 重複チェック: ユニーク制約により、同じ `global_club_id` を持つ複数の Dojo は作成できない
109+
- **ユニーク制約**: 同じ `global_club_id` を持つ複数の Dojo は作成できない
110+
- **NOT NULL 制約**: すべての Dojo に `global_club_id` が必須
111+
- **手動修正可能**: 必要に応じて管理画面から修正
112+
113+
114+
## 成功指標
115+
116+
1. **global_club_id カラムの追加完了**
117+
2. **既存 Dojo への ID 設定完了**
118+
3. **DojoMap への自動反映の実現**(Issue #1616 の目的)
119+
120+
## シンプルなワークフロー
120121

121-
### エラー処理
122-
- ネットワークエラー: 自動リトライ(最大 3 回)
123-
- API エラー: エラーログに記録し、次回の同期で再試行
124-
- マッチング失敗: 手動レビューキューに追加
122+
### 新規 Dojo 追加時(申請フォームで対応)
123+
124+
```
125+
1. 申請時に Clubs DB での登録状況を確認してもらう
126+
2. global_club_id を申請フォームに記入(必須)
127+
3. db/dojos.yaml に 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.yaml に global_club_id を追加
152+
# 例: global_club_id: "18704b53-1042-4464-9d49-8820c6ff8c97"
153+
```
125154

126-
## 今後の拡張可能性
127155

128-
- DojoMap との直接連携
129-
- リアルタイム同期(Webhook 対応)
130-
- 他の組織のクラブとの連携
131156

132157
## 更新履歴
133158

134159
- 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

Comments
 (0)