Skip to content

購入API: 残高不足が500で返る → 400 (ErrInsufficientBalance) にマッピングする #13

@ayuayuyu

Description

@ayuayuyu

概要

購入作成 API (POST /purchase) で残高不足が 500 Internal Server Error で返る。残高不足はユーザー起因の正常な業務結果であり、サーバー障害ではないため 400 Bad Request で返すべき。

これは #9 (fix/item-id-validation-400) のクライアント起因エラーを 400 に寄せる方針と同じカテゴリの問題(同 PR のスコープ外として切り出し)。

現状の挙動

  • app/domain/service/purchase/can_purchase.goCanPurchase は残高不足時に素の errors.New("insufficient balance") を返す
  • app/usecase/purchase/create_purchase.go:75 でそのエラーがそのまま伝播
  • app/ui/api/purchase/purchase.go:55-62 のハンドラで ErrItemNotFound 以外は全て else 分岐に落ち、500 になる

何が問題か

  • フロントが「残高不足(ユーザー起因)」と「本当のサーバーエラー」を区別できない
  • 500 はリトライや障害アラートを誘発する(残高不足はリトライしても無意味)
  • OpenAPI の購入 POST には既に 400 が定義済み

提案する修正

既存の ErrItemNotFoundcreate_purchase.go:13)と同じ sentinel error パターンに揃える:

  1. usecase/purchaseErrInsufficientBalance sentinel を追加
  2. usecase 側で CanPurchase の残高不足エラーを ErrInsufficientBalance でラップ(または CanPurchase 自体が sentinel を返す)
  3. ハンドラで errors.Is(err, purchase.ErrInsufficientBalance)400 にマッピング

参考・優先度低(別issue候補)

入力検証エラー(NewPurchaseItemitemID/quantity <= 0 等のドメイン検証失敗)も同様に 500 になる。ただしこれは RegisterUser(ドメイン検証 → 500)と同じ既存慣習で、上記 PR の変更が壊したものではない。「全体でドメイン検証エラーを 400 に統一するか」という設計判断の話なので、本issueとは分けて検討する。

関連ファイル

  • app/domain/service/purchase/can_purchase.go
  • app/usecase/purchase/create_purchase.go
  • app/ui/api/purchase/purchase.go
  • docs/openapi.yaml

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions