# 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}/tags` disabled 보호 보강 (기존 route) - `DELETE /v1/portfolios/{portfolio_id}/tags/{tag_id}` disabled 보호 보강 (기존 route) ## Key Decisions 1. **GET tags 응답 schema**: `PortfolioTagListResponse(tags: list[TagItem], total: int)` — 기존 `TagItem` 재사용. `portfolio.tag_ids` → `repo.get_tags_by_ids(tag_ids)` → dict → TagItem list. 2. **Disabled 보호 위치**: ownership 검증 직후, mutation 수행 직전. `if portfolio.status == "disabled": raise HTTPException(409, "disabled portfolio")`. GET은 차단 없음. 3. **Comment 작성자 ownership**: portfolio ownership만 적용 (REQ-031 Notes 결정). `_get_portfolio_or_404` 헬퍼로 일괄 처리. PATCH/DELETE 시 comment 작성자 ID 별도 검증 없음 — portfolio owner이면 모든 comment 수정/삭제 가능. 4. **Comment schema**: v1 전용 클래스 신설. `PortfolioCommentItem.comment_id` = `PortfolioComment.id`(domain) 매핑. 5. **Comment PATCH/DELETE 404**: `repo.update_comment` / `repo.delete_comment`가 `ValueError("댓글을 찾을 수 없습니다")` 발생 시 HTTP 404로 변환. 6. **Auth guard**: 기존 tag 라우트는 `require_trade_write` + `token_source != "db"` 패턴 유지. 신규 comment 라우트는 `require_read` + `_require_write_token(auth)` (write) / inline service·client 차단 (GET) 패턴 적용. 7. **Import 추가**: `PortfolioComment` domain 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가 아닌 경우 → repo `ValueError` → 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 보존) 추가