dcy-003/004 기반 v1 API surface 완성 및 BAT-008~011 구현·검증 완료:
- BAT-008 (REQ-019~021,024): 토큰 surface baseline 문서 갱신
- BAT-009 (REQ-025~028): POST/PATCH/DELETE/GET /v1/portfolios CRUD 신설
- _require_write_token, _get_portfolio_or_404, soft delete, daemon-scoped list
- BAT-010 (REQ-030~032): portfolio tag/comment 서브 라우트 신설 + baseline 갱신
- GET/POST/DELETE /v1/portfolios/{id}/tags
- POST/GET/PATCH/DELETE /v1/portfolios/{id}/comments
- BAT-011 (REQ-029): ledger path migration
- POST /v1/assets/{asset_id}/ledger/trades → /v1/portfolios/{portfolio_id}/ledger/trades
- v1_ledger.py 제거, disabled 차단(409) 추가
- test_v1.py / test_daemon_assignment.py 구 path 갱신
- 신규 tests: test_v1_portfolios_crud.py, test_v1_portfolios_list.py,
test_v1_portfolios_tags_comments.py, test_v1_portfolios_ledger.py
- 전체 suite: 340 passed
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
3.9 KiB
3.9 KiB
BAT-010 Design
- Status: confirmed
- Batch: BAT-010
Architecture Summary
app/api/v1_portfolios.py에 portfolio 서브 리소스 라우트 7개(GET tags, POST/GET/PATCH/DELETE comments)를 추가하고 기존 POST/DELETE tag 라우트에 disabled 보호를 보강한다.- 신규 v1 schema 5종(
PortfolioTagListResponse,PortfolioCommentItem,PortfolioCommentRequest,PortfolioCommentUpdateRequest,PortfolioCommentListResponse)을app/schemas/v1.py에 추가한다. docs/baseline 4종을 v1 portfolio surface에 맞게 갱신한다.
Changed Areas
- Docs:
docs/interface-contract.md— v1 portfolio CRUD + tag/comment 서브 라우트 섹션 추가; ledger path "pending REQ-029" 마킹docs/runtime-flows.md— portfolio mutation flow + GET 토큰 분기 + soft delete flow 추가docs/data-model.md— Portfolio.status='disabled' 의미 + 의존 데이터 보존 정책 추가docs/project-structure.md— app/api/v1_portfolios.py, app/schemas/v1.py 책임 기술 갱신
- Modules:
app/api/v1_portfolios.py— 7개 라우트 추가 + 기존 2개 보강app/schemas/v1.py— 5개 schema 추가
- Interfaces:
GET /v1/portfolios/{portfolio_id}/tags(신설)POST /v1/portfolios/{portfolio_id}/comments(신설)GET /v1/portfolios/{portfolio_id}/comments(신설)PATCH /v1/portfolios/{portfolio_id}/comments/{comment_id}(신설)DELETE /v1/portfolios/{portfolio_id}/comments/{comment_id}(신설)POST /v1/portfolios/{portfolio_id}/tagsdisabled 보호 보강 (기존 route)DELETE /v1/portfolios/{portfolio_id}/tags/{tag_id}disabled 보호 보강 (기존 route)
Key Decisions
- GET tags 응답 schema:
PortfolioTagListResponse(tags: list[TagItem], total: int)— 기존TagItem재사용.portfolio.tag_ids→repo.get_tags_by_ids(tag_ids)→ dict → TagItem list. - Disabled 보호 위치: ownership 검증 직후, mutation 수행 직전.
if portfolio.status == "disabled": raise HTTPException(409, "disabled portfolio"). GET은 차단 없음. - Comment 작성자 ownership: portfolio ownership만 적용 (REQ-031 Notes 결정).
_get_portfolio_or_404헬퍼로 일괄 처리. PATCH/DELETE 시 comment 작성자 ID 별도 검증 없음 — portfolio owner이면 모든 comment 수정/삭제 가능. - Comment schema: v1 전용 클래스 신설.
PortfolioCommentItem.comment_id=PortfolioComment.id(domain) 매핑. - Comment PATCH/DELETE 404:
repo.update_comment/repo.delete_comment가ValueError("댓글을 찾을 수 없습니다")발생 시 HTTP 404로 변환. - Auth guard: 기존 tag 라우트는
require_trade_write+token_source != "db"패턴 유지. 신규 comment 라우트는require_read+_require_write_token(auth)(write) / inline service·client 차단 (GET) 패턴 적용. - Import 추가:
PortfolioCommentdomain model import (v1_portfolios.py), schema 5종 import.
Edge Cases
portfolio.tag_ids = []→get_tags_by_ids([])→{}→PortfolioTagListResponse(tags=[], total=0)정상- comment 404:
portfolio_id가 존재하지만comment_id가 해당 portfolio의 comment가 아닌 경우 → repoValueError→ HTTP 404 - disabled portfolio + GET comment: 허용 (AC-8). disabled portfolio + GET tags: 허용 (별도 제한 없음)
_get_portfolio_or_404가 404를 발생시키는 경우(비존재 portfolio): service/client 차단 체크 이전에 발생 가능 → 기존 404 응답이 먼저 반환됨 (AC-11 충족)
Architecture Impact
- both (project-structure + runtime-flows 갱신)
Reference Doc Update Plan
- project-structure.md: app/api/v1_portfolios.py에 신규 라우트 기술 추가; app/schemas/v1.py schema 목록 갱신
- runtime-flows.md: portfolio mutation flow (ownership + disabled 보호), GET /v1/portfolios 토큰 분기 flow, soft delete flow (status='disabled' + dependent data 보존) 추가