|
| 1 | +# Design Doc : Improve Directive Override |
| 2 | + |
| 3 | +## Background |
| 4 | + |
| 5 | +現在の go-graphql-federation-gateway は、`@override` ディレクティブのパース機能は実装されているものの、クエリプランニング時にフィールドの所有権オーバーライドを正しく適用する検証テストが不足しています。これにより、`@override(from: "subgraphA")` で指定されたフィールドが、実際にサブグラフBで解決されるべきところ、元のサブグラフAに送信されてしまう可能性があります。 |
| 6 | + |
| 7 | +Apollo Federation v2 の仕様では、`@override` ディレクティブはフィールドの所有権を別のサブグラフに移譲するために使用されます。例えば、Product.description フィールドが元々 products サービスで定義されていたが、後に catalog サービスに移行する場合、catalog サービスで `description: String! @override(from: "products")` と定義することで、Gateway は catalog サービスから description を取得するようになります。 |
| 8 | + |
| 9 | +## Summary |
| 10 | + |
| 11 | +このドキュメントでは、`@override` ディレクティブの動作を検証し、フィールドの所有権オーバーライドが正しく機能することを確保するための設計方針と実装アプローチを提案します。具体的には、`super_graph_v2.go` の `buildOwnershipMap()` が @override を正しく処理していることの検証、および `planner_v2.go` がオーバーライドされたフィールドを正しいサブグラフに送信することの確認を行います。 |
| 12 | + |
| 13 | +## Goals |
| 14 | + |
| 15 | +- `@override` ディレクティブによるフィールド所有権の移譲が正しく動作することの検証 |
| 16 | +- 複数サブグラフでの順次オーバーライド(A→B→C)のサポート確認 |
| 17 | +- オーバーライド元サブグラフへのクエリが送信されないことの確認 |
| 18 | + |
| 19 | +## Non-Goals |
| 20 | + |
| 21 | +- `@override` の段階的ロールアウト機能(progressive @override)の実装 |
| 22 | +- `@override` のバリデーションエラー検出機能(存在しないサブグラフ名の検出など) |
| 23 | +- スキーマ変更時の自動マイグレーション機能 |
| 24 | + |
| 25 | +## Algorithm |
| 26 | + |
| 27 | +### 現在の実装状況 |
| 28 | + |
| 29 | +**パース機能(実装済み):** |
| 30 | + |
| 31 | +```go |
| 32 | +// subgraph_v2.go:204-210 |
| 33 | +case "override": |
| 34 | + // Parse from argument of @override directive |
| 35 | + for _, arg := range d.Arguments { |
| 36 | + if arg.Name.String() == "from" { |
| 37 | + from := strings.Trim(arg.Value.String(), "\"") |
| 38 | + f.Override = &OverrideMetadata{From: from} |
| 39 | + } |
| 40 | + } |
| 41 | +``` |
| 42 | + |
| 43 | +**所有権マップ構築(実装済み):** |
| 44 | + |
| 45 | +```go |
| 46 | +// super_graph_v2.go:367-407 |
| 47 | +// buildOwnershipMap() で @override を考慮 |
| 48 | +if field.Override != nil { |
| 49 | + // @override(from: "subgraphName") がある場合、 |
| 50 | + // 元のサブグラフを所有権マップから除外 |
| 51 | + delete(ownershipMap, field.Override.From) |
| 52 | +} |
| 53 | +``` |
| 54 | + |
| 55 | +### 検証が必要な動作 |
| 56 | + |
| 57 | +```mermaid |
| 58 | +flowchart TD |
| 59 | + Start([buildOwnershipMap]) --> Loop{全サブグラフを走査} |
| 60 | + Loop -- 次の SubGraph --> CheckFields{フィールドを走査} |
| 61 | + CheckFields -- 次のフィールド F --> HasOverride{F に @override あり?} |
| 62 | + HasOverride -- Yes --> RemoveOriginal[ownershipMap から<br>F.Override.From を削除] |
| 63 | + RemoveOriginal --> AddNew[現在のサブグラフを<br>ownershipMap に追加] |
| 64 | + AddNew --> CheckFields |
| 65 | + HasOverride -- No --> AddNormal[通常通り<br>ownershipMap に追加] |
| 66 | + AddNormal --> CheckFields |
| 67 | + CheckFields -- 完了 --> Loop |
| 68 | + Loop -- 完了 --> End([終了]) |
| 69 | +``` |
| 70 | + |
| 71 | +### 修正箇所 1: super_graph_v2_test.go の検証テスト追加 |
| 72 | + |
| 73 | +**追加するテスト:** |
| 74 | + |
| 75 | +```go |
| 76 | +// TestSuperGraphV2_Override_BasicOverride |
| 77 | +// - productsサービスで定義されたフィールドをcatalogサービスでオーバーライド |
| 78 | +// - GetSubGraphsForField() がcatalogのみを返すこと |
| 79 | + |
| 80 | +// TestSuperGraphV2_Override_ChainedOverride |
| 81 | +// - A→B→Cの順次オーバーライド |
| 82 | +// - 最終的なオーナーがCになること |
| 83 | + |
| 84 | +// TestSuperGraphV2_Override_PartialFields |
| 85 | +// - 一部のフィールドのみオーバーライド |
| 86 | +// - オーバーライドされていないフィールドは元のサブグラフが所有 |
| 87 | +``` |
| 88 | + |
| 89 | +### 修正箇所 2: planner_v2_override_test.go の結合テスト追加 |
| 90 | + |
| 91 | +**追加するテスト:** |
| 92 | + |
| 93 | +```go |
| 94 | +// TestPlannerV2_Override_QueryRouting |
| 95 | +// - クエリが正しいサブグラフにルーティングされること |
| 96 | +// - オーバーライド元サブグラフへのクエリが生成されないこと |
| 97 | + |
| 98 | +// TestPlannerV2_Override_WithEntityFetch |
| 99 | +// - エンティティフェッチ時のオーバーライド動作 |
| 100 | +// - @override されたフィールドが正しいサブグラフで解決されること |
| 101 | +``` |
| 102 | + |
| 103 | +--- |
| 104 | + |
| 105 | +## Request Sequence |
| 106 | + |
| 107 | +### 基本的な @override の動作 |
| 108 | + |
| 109 | +```mermaid |
| 110 | +sequenceDiagram |
| 111 | + participant Client |
| 112 | + participant Gateway |
| 113 | + participant Catalog |
| 114 | + participant Products |
| 115 | +
|
| 116 | + Note over Products: 旧実装: description フィールドを所有 |
| 117 | + Note over Catalog: 新実装: description @override(from: "products") |
| 118 | +
|
| 119 | + Client->>Gateway: query { product { id name description } } |
| 120 | + Note over Gateway: buildOwnershipMap():<br>description の所有権は catalog |
| 121 | + Gateway->>Catalog: query { product { id name description } } |
| 122 | + Note over Gateway: products サービスにはクエリを送信しない |
| 123 | + Catalog-->>Gateway: { id: "p1", name: "Widget", description: "..." } |
| 124 | + Gateway->>Client: { product: { id, name, description } } |
| 125 | +``` |
| 126 | + |
| 127 | +### 順次オーバーライド (A→B→C) |
| 128 | + |
| 129 | +```mermaid |
| 130 | +sequenceDiagram |
| 131 | + participant Client |
| 132 | + participant Gateway |
| 133 | + participant ServiceC |
| 134 | + participant ServiceB |
| 135 | + participant ServiceA |
| 136 | +
|
| 137 | + Note over ServiceA: 最初の実装 |
| 138 | + Note over ServiceB: @override(from: "serviceA") |
| 139 | + Note over ServiceC: @override(from: "serviceB") |
| 140 | +
|
| 141 | + Client->>Gateway: query { product { field } } |
| 142 | + Note over Gateway: buildOwnershipMap():<br>最終的な所有者は serviceC |
| 143 | + Gateway->>ServiceC: query { product { field } } |
| 144 | + ServiceC-->>Gateway: { field: "value" } |
| 145 | + Gateway->>Client: { product: { field: "value" } } |
| 146 | +``` |
| 147 | + |
| 148 | +--- |
| 149 | + |
| 150 | +## Development Command For AI Agent |
| 151 | + |
| 152 | +### Process |
| 153 | + |
| 154 | +**重要:** 以下のプロセスは TDD(テスト駆動開発)を厳守すること。既存の実装を検証するためのテストを先に書き、Red → Green → Refactor のサイクルを回すこと。 |
| 155 | + |
| 156 | +1. **SuperGraph V2 テスト追加 (TDD)** |
| 157 | + 1.1. **RED: テストを先に書く** - `super_graph_v2_test.go` |
| 158 | + - 基本的な @override の所有権移譲テスト |
| 159 | + - 順次オーバーライド(A→B→C)テスト |
| 160 | + - 部分的なフィールドオーバーライドテスト |
| 161 | + - テストを実行(既存の実装で成功するはずだが、念のため確認) |
| 162 | + 1.2. **GREEN: 実装の検証と修正** |
| 163 | + - 既存の実装で全テストが通ることを確認 |
| 164 | + - テストが失敗した場合は実装を修正 |
| 165 | + - テストカバレッジは 95% 以上を目指す |
| 166 | + 1.3. **REFACTOR: リファクタリング** |
| 167 | + - 必要に応じてコードを改善 |
| 168 | + - テストが引き続き成功することを確認 |
| 169 | + |
| 170 | +2. **Planner V2 テスト追加 (TDD)** |
| 171 | + 2.1. **RED: テストを先に書く** - `planner_v2_override_test.go` (新規作成) |
| 172 | + - @override されたフィールドへのクエリが正しいサブグラフに送信されることのテスト |
| 173 | + - オーバーライド元サブグラフへのクエリが生成されないことのテスト |
| 174 | + - エンティティフェッチ時の @override 動作テスト |
| 175 | + - テストを実行(既存の実装で成功するはずだが、念のため確認) |
| 176 | + 2.2. **GREEN: 実装の検証と修正** |
| 177 | + - 既存の実装で全テストが通ることを確認 |
| 178 | + - テストが失敗した場合は実装を修正 |
| 179 | + 2.3. **REFACTOR: リファクタリング** |
| 180 | + - 必要に応じてコードを改善 |
| 181 | + - テストが引き続き成功することを確認 |
| 182 | + |
| 183 | +3. **結合テスト** |
| 184 | + 3.1. `_example` にオーバーライドシナリオを追加(オプション) |
| 185 | + 3.2. `make test-all` で全ドメインのテストが通ることを確認 |
| 186 | + |
| 187 | +**TDD チェックリスト:** |
| 188 | +- [ ] 各機能について、実装前(または既存実装の検証前)にテストを書いたか? |
| 189 | +- [ ] テストを実行して、期待通りの結果が得られることを確認したか? |
| 190 | +- [ ] テストが失敗した場合は実装を修正したか?(GREEN) |
| 191 | +- [ ] リファクタリング後もテストが成功することを確認したか?(REFACTOR) |
| 192 | +- [ ] 全てのテストが通ることを確認したか? |
| 193 | + |
| 194 | +### Expected Outcomes |
| 195 | + |
| 196 | +- `@override` ディレクティブの動作が包括的にテストされる |
| 197 | +- フィールドの所有権移譲が正しく機能することが保証される |
| 198 | +- 回帰テストとして将来の変更を保護できる |
0 commit comments