|
8 | 8 | .agent/ |
9 | 9 | ├── specs/ # 仕様・設計の単一ソース(SoT) |
10 | 10 | ├── docs/ |
11 | | - │ ├── qa-index.md # QA インデックス(リリース時に更新) |
| 11 | + │ ├── qa.md # QA インデックス(リリース時に更新) |
12 | 12 | │ ├── tasks.md # タスクリストインデックス(リリース時に更新) |
13 | 13 | │ ├── qa/ # バージョン別 QA |
14 | 14 | │ ├── tasks/ # バージョン別タスクリスト |
|
35 | 35 |
|
36 | 36 | このディレクトリで管理するドキュメント: |
37 | 37 |
|
38 | | -#### **QA_AND_DECISIONS.md** - Q&A と設計決定ログ |
| 38 | +#### **qa.md** - Q&A と設計決定ログ |
39 | 39 | - 形式: `## [カテゴリ] 質問タイトル` |
40 | 40 | - カテゴリ例: `[API]`, `[UI]`, `[アーキテクチャ]`, `[テスト]`, `[データベース]` など |
41 | 41 | - 推奨フォーマット: |
|
51 | 51 | ``` |
52 | 52 | - 目的: 設計決定とアーキテクチャの議論を記録し、将来の参照用として保管 |
53 | 53 |
|
54 | | -**QA ファイル分割ルール(v1.20 より採用)**: |
| 54 | +**QA ファイル分割ルール**: |
55 | 55 |
|
56 | 56 | QA リストはコンフリクト防止のため、バージョン毎に分割。tasks.md と同じ構造を採用。 |
57 | 57 |
|
58 | 58 | **ディレクトリ構成**: |
59 | 59 | ``` |
60 | 60 | .agent/docs/ |
61 | | -├── qa-index.md ← インデックス(tasks.md 相当) |
62 | | -├── QA_AND_DECISIONS.md ← レガシー(v1.19 以前用) |
| 61 | +├── qa.md ← インデックス(tasks.md 相当) |
63 | 62 | └── qa/ |
64 | 63 | ├── v1.20-qa.md ← v1.20 開発中の Q&A・決定 |
65 | 64 | ├── v1.21-qa.md ← v1.21 の Q&A(予定) |
@@ -191,134 +190,6 @@ feat: 日付範囲によるドリンクフィルタリングを追加 |
191 | 190 |
|
192 | 191 | ## 🏗️ 実装ガイドライン |
193 | 192 |
|
194 | | -### Store 実装パターン |
195 | | - |
196 | | -**責務分離**: Data Stores と Page Stores を厳格に分離 |
197 | | - |
198 | | -#### Data Stores(`store/data/`) |
199 | | -```ts |
200 | | -// 単純な CRUD + リポジトリ呼び出し |
201 | | -const fetchDrinks = async () => { |
202 | | - try { |
203 | | - drinks.value = await $drinksRepository.fetchAll() |
204 | | - } catch (error) { |
205 | | - // 単に再スロー(トーストなし) |
206 | | - throw error |
207 | | - } |
208 | | -} |
209 | | - |
210 | | -// 状態は readonly で公開 |
211 | | -return { |
212 | | - drinks: readonly(drinks), |
213 | | - fetchDrinks, |
214 | | -} |
215 | | -``` |
216 | | - |
217 | | -**ルール**: |
218 | | -- ✅ リポジトリを直接呼び出し |
219 | | -- ✅ エラーはそのまま再スロー |
220 | | -- ✅ トーストメッセージは表示しない |
221 | | -- ✅ 状態は `readonly()` で公開 |
222 | | -- ❌ 複数ストアの組み合わせなし |
223 | | -- ❌ ビジネスロジック・集約なし |
224 | | - |
225 | | -#### Page Stores(`store/pages/`) |
226 | | -```ts |
227 | | -// 複数の data store + エラー処理 + トースト表示 |
228 | | -const fetchData = async () => { |
229 | | - try { |
230 | | - showLoading() |
231 | | - await drinksStore.fetchDrinks() |
232 | | - // データ集約・変換処理 |
233 | | - } catch (error) { |
234 | | - if (error instanceof CustomError) { |
235 | | - showDangerToast(error.getMessage()) |
236 | | - } |
237 | | - logger.error('Failed to fetch', { module: 'indexStore' }, error) |
238 | | - } finally { |
239 | | - hideLoading() |
240 | | - } |
241 | | -} |
242 | | -``` |
243 | | - |
244 | | -**ルール**: |
245 | | -- ✅ 複数のデータストアを組み合わせ |
246 | | -- ✅ エラーをキャッチしてトーストを表示 |
247 | | -- ✅ 集約・変換ロジックを実装 |
248 | | -- ✅ ログレコーディングを実施 |
249 | | -- ✅ ローディング状態を管理 |
250 | | -- ❌ リポジトリの直接呼び出しなし |
251 | | -- ❌ エラー処理なしの再スロー |
252 | | - |
253 | | -### エラーハンドリング階層図 |
254 | | - |
255 | | -``` |
256 | | -Component |
257 | | - ↓ (store action 呼び出し) |
258 | | -Page Store ← トーストを表示、ログ記録 |
259 | | - ↓ (data store 呼び出し) |
260 | | -Data Store ← 単に再スロー |
261 | | - ↓ (repository 呼び出し) |
262 | | -Repository ← CustomError をスロー |
263 | | - ↓ |
264 | | -Cloudflare API |
265 | | -``` |
266 | | - |
267 | | -**各層の責務**: |
268 | | - |
269 | | -1. **Repository** (`app/utils/api/`) |
270 | | - - Cloudflare API エラーをキャッチ |
271 | | - - `CustomError` に変換してスロー |
272 | | - - メッセージは実装者向け(英語) |
273 | | - |
274 | | -2. **Data Store** (`store/data/`) |
275 | | - - エラーを単に再スロー |
276 | | - - 処理なし(Page Store に任せる) |
277 | | - |
278 | | -3. **Page Store** (`store/pages/`) |
279 | | - - エラーをキャッチ |
280 | | - - `showDangerToast()` でユーザーに通知 |
281 | | - - `logger.error()` でログ記録 |
282 | | - - 必要に応じて UI 状態をリセット |
283 | | - |
284 | | -4. **Component** (`app/components/`) |
285 | | - - Page Store action を呼び出し |
286 | | - - ストア経由でユーザーに通知 |
287 | | - - 直接的なエラー処理は不要 |
288 | | - |
289 | | -**トースト種類**: |
290 | | -```ts |
291 | | -showSuccessToast('操作が成功しました') // 緑 |
292 | | -showInfoToast('確認メッセージです') // 青 |
293 | | -showWarningToast('注意が必要です') // 黄 |
294 | | -showDangerToast('エラーが発生しました') // 赤 |
295 | | -``` |
296 | | - |
297 | | -**例**: 飲み物削除 |
298 | | -```ts |
299 | | -// Page Store: deleteAction |
300 | | -const deleteDrink = async (id: number, name: string) => { |
301 | | - try { |
302 | | - showLoading() |
303 | | - await drinksStore.deleteDrink(id) |
304 | | - showSuccessToast( |
305 | | - t(LOCALE_DRINKS_DELETE_SUCCESS, { name }) |
306 | | - ) |
307 | | - await fetchDrinks() // リロード |
308 | | - } catch (error) { |
309 | | - if (error instanceof CustomError) { |
310 | | - showDangerToast(error.getMessage()) |
311 | | - } |
312 | | - logger.error('Failed to delete drink', |
313 | | - { module: 'drinksStore', drinkId: id }, |
314 | | - error |
315 | | - ) |
316 | | - } finally { |
317 | | - hideLoading() |
318 | | - } |
319 | | -} |
320 | | -``` |
321 | | - |
322 | 193 | ### PR レビューチェックリスト |
323 | 194 |
|
324 | 195 | PR を作成またはレビューする際は、以下を確認してください: |
@@ -388,7 +259,6 @@ PR を作成またはレビューする際は、以下を確認してくださ |
388 | 259 | - ❌ 実装なしで仕様を作成する |
389 | 260 | - ❌ 複数のファイルに情報を重複させる |
390 | 261 | - ❌ GitHub Issues へのリンクを忘れる |
391 | | -- ❌ 解決済みの質問を `.agent/docs/` に無期限に残す |
392 | 262 |
|
393 | 263 | --- |
394 | 264 |
|
|
0 commit comments