Files
portfolio-hub/docs/batches/bat-011_20260521_v1-portfolios-ledger-path-migration/design.md
T
genichinandClaude Sonnet 4.6 4bf6b554a7 feat: v1 portfolio mutation, sub-resources, ledger path migration (BAT-008~011)
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>
2026-05-21 07:25:39 +00:00

2.9 KiB

BAT-011 Design

  • Status: confirmed
  • Batch: BAT-011

Architecture Summary

  • app/api/v1_portfolios.py에 POST /{portfolio_id}/ledger/trades 서브 라우트를 추가한다. 기존 app/api/v1_ledger.py의 daemon 인가 3단계 + idempotency 로직을 신 path로 이식하고, disabled 차단을 daemon 인가 이전에 삽입한다.
  • 구 path는 파일 전체 제거(app/api/v1_ledger.py 삭제) + app/main.py에서 include 제거로 처리한다. 별칭/redirect 없음.
  • trex-basic 동시 배포 순서: trex-basic 사전 빌드 완료 확인 → portfolio-hub 배포 → trex-basic 배포. rollback 시 portfolio-hub + trex-basic 동반 rollback.

Changed Areas

  • Docs:
    • docs/interface-contract.md — REQ-032에서 이미 "pending REQ-029" 마킹됨. 본 batch에서 실제 명세 반영.
    • docs/runtime-flows.md — ledger trade path 이전 흐름 + daemon assignment 3단계 인가 갱신 (REQ-032 기반 기존 흐름 보강).
  • Modules:
    • app/api/v1_portfolios.py — 신 path 라우트 추가
    • app/api/v1_ledger.py — 제거 (구 path 라우트 삭제)
    • app/main.py — 구 ledger 라우터 include 제거
    • app/services/ledger_service.py — disabled 차단 검증 추가 (또는 라우터 계층에서 처리)
  • Interfaces:
    • POST /v1/portfolios/{portfolio_id}/ledger/trades (신설)
    • POST /v1/assets/{asset_id}/ledger/trades (제거 → HTTP 404)

Key Decisions

  1. 구 v1_ledger.py 완전 제거: 라우트만 제거하는 대신 파일 전체를 삭제해 잔재 코드가 남지 않도록 한다. app/main.py에서 해당 라우터 include를 제거.
  2. disabled 차단 위치: 라우터 계층에서 _get_portfolio_or_404 직후, daemon 인가 3단계 이전에 if portfolio.status == "disabled": raise HTTPException(409) 삽입. 서비스 계층 변경 최소화.
  3. 인가 순서: (0) Bearer 없음 → 401, (1) token daemon_id IS NULL → 403, (2) portfolio not found → 404, (3) portfolio.status == "disabled" → 409, (4) portfolio.managed_by_daemon_id IS NULL → 403, (5) portfolio.managed_by_daemon_id != auth.daemon_id → 403, (6) idempotency 분기 → 201/200/409.
  4. trex-basic 동시 배포: release plan에 배포 순서 잠금 명시. 한시적 별칭/redirect 없음.

Edge Cases

  • portfolio_id가 비존재 → HTTP 404 (daemon 인가보다 우선).
  • status='disabled' portfolio → HTTP 409 (daemon 인가 체크 전에 차단하여 daemon 미배정 오류와 구분).
  • 동일 (portfolio_id, order_id, execution_id) + 동일 payload 재호출 → HTTP 200 + status="duplicate".
  • 동일 key + 다른 payload → HTTP 409 (idempotency conflict).
  • Bearer token 없음 → HTTP 401.

Architecture Impact

  • runtime-flows

Reference Doc Update Plan

  • project-structure.md: v1_ledger.py 제거 반영
  • runtime-flows.md: ledger path 이전 흐름 + daemon 인가 3단계 + disabled 차단 순서 갱신