From d7bcb7eb0c55a944cb545779ab70780198ccea50 Mon Sep 17 00:00:00 2001 From: Yohei Yasukawa Date: Sun, 20 Jul 2025 19:25:49 +0900 Subject: [PATCH 1/2] =?UTF-8?q?=F0=9F=93=9D=20Global=20Clubs=E9=80=A3?= =?UTF-8?q?=E6=90=BA=E6=A9=9F=E8=83=BD=E3=81=AE=E8=A8=AD=E8=A8=88=E3=83=89?= =?UTF-8?q?=E3=82=AD=E3=83=A5=E3=83=A1=E3=83=B3=E3=83=88=E3=82=92=E8=BF=BD?= =?UTF-8?q?=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Raspberry Pi FoundationのClubs APIとCoderDojo Japanのデータベースを 連携させる機能の設計と実装計画をドキュメント化。 主な内容: - 背景と現状の課題 - 命名規則の決定理由(global_club_id, GlobalClubsモジュール) - YAGNI原則に基づくシンプルな実装方針 - 技術仕様(暫定版)と実装計画 refs #1616 --- docs/global_clubs_integration.md | 134 +++++++++++++++++++++++++++++++ 1 file changed, 134 insertions(+) create mode 100644 docs/global_clubs_integration.md diff --git a/docs/global_clubs_integration.md b/docs/global_clubs_integration.md new file mode 100644 index 000000000..3be336ace --- /dev/null +++ b/docs/global_clubs_integration.md @@ -0,0 +1,134 @@ +# Global Clubs Integration + +## 概要 + +このドキュメントは、Raspberry Pi Foundation が管理する国際的な Clubs API データベースと CoderDojo Japan のデータベースを連携させる機能の設計と実装について記載しています。 + +## 背景 + +### 現状の課題 +- 新しい Dojo が追加されるたびに、DojoMap に表示させるために手動で CSV を更新する必要がある +- この手動作業は時間がかかり、エラーが発生しやすい + +### 解決策 +Global Clubs データベースの ID(`global_club_id`)を CoderDojo Japan の Dojo と紐付けることで、データの同期を自動化する。 + +## 設計決定 + +### 1. 命名規則 + +#### `global_club_id` を選択した理由 +- `club_id` では `dojo_id` との違いが不明瞭 +- `global_` プレフィックスにより、外部システムの ID であることが明確 +- 将来的に状況が変わっても、名前を変更する必要がない + - 想定ケース: CoderDojo Foundation (Zen API) -> Raspberry Pi Foundation (Clubs API) -> ??? (FooBar API) + +#### `GlobalClubs` モジュール名を選択した理由 +- 現在は Raspberry Pi Foundation が管理しているが、過去には CoderDojo Foundation (Zen) が管理していた +- 将来的に同じような変更が行われる可能性を考慮 (想定例: GraphQL の廃止、REST API への回帰、など) +- 実装は現在の API(Raspberry Pi)に直接接続するが、命名は抽象的に保つ + +### 2. 実装方針 + +#### YAGNI 原則の適用 +当初はアダプターパターンを検討したが、以下の理由でシンプルな実装を選択: +- API プロバイダーの変更は頻繁には起こらない(もしくは永遠に起こらない) +- 過度な抽象化は開発速度を低下させる +- 必要になったときにリファクタリングすればよい + +#### API メソッドの命名 +```ruby +def fetch_global_clubs(organization_slug: 'coderdojo', after: nil) +``` +- `fetch_coderdojo_clubs` ではなく `fetch_global_clubs` を選択 +- API は CoderDojo 以外の組織のクラブも返すため、より正確な命名 +- `organization_slug` パラメータでフィルタリング可能 + +## 技術仕様 + +> **⚠️ 注意**: この節以降の内容は実装過程で変更される可能性があります。実際の実装とレビューを経て確定します。(2025-01-20時点) + +### API エンドポイント +- GraphQL: `https://clubs-api.raspberrypi.org/graphql` +- 認証: Bearer token (OAuth flow) +- レート制限: 60 req/min + +### データモデル + +#### Dojo モデルの拡張 +```ruby +# マイグレーション +add_column :dojos, :global_club_id, :bigint +add_index :dojos, :global_club_id, unique: true, where: 'global_club_id IS NOT NULL' +``` + +#### 同期管理テーブル +```ruby +create_table :global_club_syncs do |t| + t.bigint :global_club_id, null: false + t.datetime :last_seen_at + t.string :sync_status + t.timestamps +end +``` + +### 同期戦略 + +#### 増分同期 +- `updatedAt` フィールドを使用して、前回の同期以降に更新されたクラブのみを取得 +- 全データを毎回取得するのではなく、効率的な差分更新を実現 +- 冪等性を保証(同じ操作を複数回実行しても結果が同じ) + +#### マッチングアルゴリズム +1. `global_club_id` で既存の Dojo を検索 +2. 見つからない場合は、名前の類似度 + 地理的距離(1km 以内)で自動マッチング +3. 曖昧な場合は手動レビューキューに追加 + +## 実装計画 + +### フェーズ 1: 基盤整備 +1. データベーススキーマの更新(`global_club_id` カラム追加) +2. YAML ファイルの構造更新 +3. モデルのバリデーション追加 + +### フェーズ 2: API 統合 +1. `GlobalClubs::Client` クラスの実装 +2. GraphQL クエリの実装 +3. 認証設定(Rails credentials) + +### フェーズ 3: 同期機能 +1. `GlobalClubs::SyncService` の実装 +2. マッチングロジックの実装 +3. エラーハンドリングとリトライ機構 + +### フェーズ 4: 運用 +1. 定期実行ジョブの設定 +2. 監視とアラートの実装 +3. 手動レビュー機能(将来的に) + +## セキュリティ考慮事項 + +- Bearer token は Rails credentials に保存 +- API 通信は HTTPS のみ +- レート制限に対応(指数バックオフでリトライ) + +## 運用上の注意点 + +### データの整合性 +- ソフトデリート: Global Clubs API から削除されたクラブは `global_club_id = nil` に設定(Dojo レコードは削除しない) +- 重複チェック: ユニーク制約により、同じ `global_club_id` を持つ複数の Dojo は作成できない + +### エラー処理 +- ネットワークエラー: 自動リトライ(最大 3 回) +- API エラー: エラーログに記録し、次回の同期で再試行 +- マッチング失敗: 手動レビューキューに追加 + +## 今後の拡張可能性 + +- DojoMap との直接連携 +- リアルタイム同期(Webhook 対応) +- 他の組織のクラブとの連携 + +## 更新履歴 + +- 2025-01-20: 初版作成(設計決定まで確定、技術仕様以降は暫定版) From 55c46b3e363b6a620a8a467376a80a96d8e0c993 Mon Sep 17 00:00:00 2001 From: Yohei Yasukawa Date: Sun, 20 Jul 2025 21:07:33 +0900 Subject: [PATCH 2/2] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20=E3=83=89=E3=82=AD?= =?UTF-8?q?=E3=83=A5=E3=83=A1=E3=83=B3=E3=83=88=E5=90=8D=E3=82=92=20global?= =?UTF-8?q?=5Fclubs=5Fintegration=20=E3=81=8B=E3=82=89=20global=5Fclub=5Fi?= =?UTF-8?q?d=5Fdesign=20=E3=81=AB=E5=A4=89=E6=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 'integration'は2つのシステムを統合するような大規模な変更を 連想させるため、実際の機能(IDの紐付け)を正確に表す名前に変更。 - docs/global_clubs_integration.md → docs/global_club_id_design.md - タイトルも Global Club ID Design に更新 --- docs/{global_clubs_integration.md => global_club_id_design.md} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename docs/{global_clubs_integration.md => global_club_id_design.md} (99%) diff --git a/docs/global_clubs_integration.md b/docs/global_club_id_design.md similarity index 99% rename from docs/global_clubs_integration.md rename to docs/global_club_id_design.md index 3be336ace..b1d8e9f7c 100644 --- a/docs/global_clubs_integration.md +++ b/docs/global_club_id_design.md @@ -1,4 +1,4 @@ -# Global Clubs Integration +# Global Club ID Design ## 概要