Core Changes: - Migrate portfolio_id from string to UUID in schema and repository layer - Update portfolio CLI commands to use name+user combination for identification - Update HTTP API to accept portfolio_id as UUID path parameter - Implement portfolio ownership validation in repository queries Requirements Completed (REQ-016 to REQ-019): - REQ-016: portfolios table UUID schema migration with data migration script - REQ-017: CLI portfolio commands with name+user identification - REQ-018: HTTP API portfolio path parameters and v1 asset_id UUID returns - REQ-019: Portfolio UUID baseline documentation updates BAT-001 Re-verification: - REQ-004~010 marked as Implemented after verification evidence validation - All v1 API endpoints verified with comprehensive test coverage Documentation Updates: - Update data-model.md with portfolio UUID structure - Update interface-contract.md with UUID path parameters - Update runtime-flows.md with ownership-based resolution flows - Update project-structure.md with database layer changes - Add migration script for portfolio UUID transition Testing: - Add test_portfolio_uuid.py for migration validation - Update test_cli.py and test_portfolio_ownership.py for ownership checks
7.0 KiB
BAT-007 Design
- Status: confirmed
- Batch: BAT-007
- Profile: batch-lite
Architecture Summary
이 batch는 코드 구조 변경 없이 분석 및 문서화만 수행한다. 주요 설계 작업은 ensure_schema()와 Alembic revision 간 schema ownership 경계를 코드에서 직접 분석하고 그 결과를 baseline 문서에 반영하는 것이다.
코드 변경이 필요한 경우는 분석 결과 ownership 충돌이 발견될 때뿐이며, 이 batch 범위에서는 해당 충돌이 기존 IF NOT EXISTS/stamp 메커니즘으로 이미 처리되고 있음이 확인되었다.
Changed Areas
| 대상 | 변경 유형 | REQ |
|---|---|---|
README.md |
로컬 DB 검증 절차, runbook 추가 | REQ-001, REQ-003 |
docs/runtime-flows.md |
Local DB 초기화 흐름, Alembic/ensure_schema() 경계 흐름 추가 | REQ-001, REQ-002, REQ-003 |
docs/project-structure.md |
Schema ownership 경계 섹션 추가 | REQ-002, REQ-003 |
docker-compose.local-test.yml |
변경 없음 — 현행 구성이 검증 시나리오 요건 충족 | — |
app/cli/commands/health.py |
변경 없음 — init-db/migrate-db/health-check 출력 구조 확인 완료 | — |
app/db/migrations.py |
변경 없음 — bootstrap_or_upgrade_to_head() 흐름 분석으로 coverage 충족 | — |
app/repositories/postgres.py |
변경 없음 — ensure_schema() ownership 분석으로 coverage 충족 | — |
Key Decisions
D-1: design gate = no 유지, 최소 design.md 작성
Planning의 Design Gate: no 결정을 유지한다. 사용자가 draft-batch-design을 명시적으로 요청했으므로 이 문서를 생성하되, 신규 인터페이스/코드 구조 결정 사항이 없음을 확인하고 기록하는 용도로 사용한다.
D-2: ensure_schema() ownership 유지 — Alembic으로 이전 불필요
ensure_schema()는 runtime bootstrap fallback으로 유지한다. fresh DB → app 시작 → migrate-db 순서에서 bootstrap_or_upgrade_to_head()의 legacy stamp 경로가 이 충돌을 처리한다. 이번 batch에서 ensure_schema()를 제거하거나 Alembic으로 완전 이전하는 작업은 범위 밖이다.
D-3: 검증 시나리오를 README.md + docs/runtime-flows.md에 배분
검증 매트릭스(REQ-001)를 별도 파일로 생성하지 않고, 운영자가 참조하는 README.md와 기술적 흐름을 담는 docs/runtime-flows.md에 나누어 반영한다. 이 방식이 docs/project-structure.md 정책(docs/ = SDLC source-of-truth)과 일관된다.
Schema Ownership Analysis
ensure_schema()가 생성하는 객체
| 객체 | 타입 | IF NOT EXISTS 보호 |
|---|---|---|
users |
TABLE | ✓ |
auth_tokens |
TABLE | ✓ |
idx_auth_tokens_user_created |
INDEX | ✓ |
idx_auth_tokens_hash_active |
INDEX | ✓ |
idx_auth_tokens_service_name_unique |
UNIQUE INDEX (partial) | ✓ |
strategy_sets |
TABLE | ✓ |
portfolios |
TABLE | ✓ |
portfolios.name |
COLUMN | ADD COLUMN IF NOT EXISTS |
portfolios.owner_user_id |
COLUMN | ADD COLUMN IF NOT EXISTS |
uq_portfolios_owner_name |
UNIQUE CONSTRAINT | DO ...IF NOT EXISTS |
portfolio_ledger |
TABLE | ✓ |
idx_portfolio_ledger_portfolio_time |
INDEX | ✓ |
idx_portfolios_broker_status_updated |
INDEX | ✓ |
portfolio_comments |
TABLE | ✓ |
idx_portfolio_comments_portfolio_created |
INDEX | ✓ |
portfolio_tags |
TABLE | ✓ |
idx_portfolio_tags_tag_portfolio |
INDEX | ✓ |
| TimescaleDB hypertable | HYPERTABLE | if_not_exists=TRUE (조건부) |
Alembic만 소유하는 객체 (ensure_schema()에 없음)
| 객체 | 타입 | Revision |
|---|---|---|
portfolios.market |
COLUMN | 0014 |
auth_tokens.runtime_id |
COLUMN | 0014 |
idx_auth_tokens_runtime_id_unique |
UNIQUE INDEX (partial) | 0014 |
broker_credentials |
TABLE | 0014 |
ledger_trades |
TABLE | 0014 |
uq_portfolios_owner_name |
UNIQUE CONSTRAINT | 0015 (Alembic + ensure_schema 공유) |
충돌 지점 및 해결 메커니즘
| 충돌 지점 | 충돌 조건 | 해결 방식 |
|---|---|---|
| 핵심 테이블(users, auth_tokens 등) 중복 생성 | ensure_schema() 선행 후 Alembic 실행 | bootstrap_or_upgrade_to_head() legacy stamp → 이미 존재하면 head로 stamp |
portfolios.name / owner_user_id 컬럼 중복 |
ensure_schema() 선행 후 Alembic 0008/0010 실행 | Alembic revision이 _column_exists() guard 사용 |
uq_portfolios_owner_name 중복 |
ensure_schema() 선행 후 Alembic 0015 실행 | Alembic 0015의 _constraint_exists() guard 사용 |
| v1 API 전용 스키마 누락 | ensure_schema()만 실행한 환경 | migrate-db 실행 필요 — ensure_schema()는 v1 API 스키마를 포함하지 않음 |
허용/금지 실행 순서
허용 (safe):
init-db→ App 시작 → 정상 운영 (Alembic이 완전 스키마 생성, ensure_schema()는 no-op)- App 시작 (ensure_schema() 선행) →
init-db또는migrate-db(legacy stamp + upgrade 경로) - 이미 Alembic 관리 DB →
migrate-db(증분 upgrade) - 검증 후
migrate-db재실행 (모든 revision이 IF NOT EXISTS 보호)
주의 (caution):
- ensure_schema()만 실행한 환경 → v1 API(
broker_credentials,ledger_trades,runtime_id) 미동작 → 반드시migrate-db필요 migrate-db단독 실행 → DB 미존재 시 실패 →init-db우선 실행 필요
금지 (forbidden):
alembic_version테이블 수동 삭제 후migrate-db재실행 → legacy stamp 오판 위험
Edge Cases
- TimescaleDB 미설치 환경: ensure_schema()의 TimescaleDB hypertable 생성은
pg_extension에서 timescaledb 존재 여부를 먼저 확인한다. 미설치 시 포트폴리오 운영은 가능하나 시계열 최적화 없이 작동한다. - ensure_schema()의 v1 스키마 미포함:
broker_credentials,ledger_trades,runtime_id컬럼은 ensure_schema()에 없다. ensure_schema()만 실행한 환경에서 GET /v1/user_info 또는 POST .../ledger/trades를 호출하면 DB 오류가 발생한다.migrate-db로 해결한다. - 비어있는 alembic_version:
bootstrap_or_upgrade_to_head()는 먼저alembic_version테이블 존재를 확인한 뒤, 없으면 legacy 테이블 존재를 확인한다. fresh DB이면서 alembic_version 없으면 full upgrade 경로를 탄다.
Architecture Impact
both — docs/project-structure.md와 docs/runtime-flows.md 모두 갱신
Reference Doc Update Plan
| 문서 | 추가/수정 내용 | REQ |
|---|---|---|
README.md |
Local DB 검증 절차 섹션 추가 (준비, init-db, health-check, smoke flow, 반복 실행 기준) | REQ-001, REQ-003 |
docs/runtime-flows.md |
"Flow: Local DB Initialization and Validation" 추가 (init-db/migrate-db 흐름, Alembic/ensure_schema() 경계) | REQ-001, REQ-002, REQ-003 |
docs/project-structure.md |
"Schema Ownership Boundary" 섹션 추가 (객체별 소유 경로, 허용/금지 순서, 범위 제외 명시) | REQ-002, REQ-003 |