# BAT-006 Design - Status: confirmed - Batch: BAT-006 ## Architecture Summary `portfolios.portfolio_id`를 사용자 지정 TEXT PK에서 서버 생성 UUID(TEXT)로 교체한다. 기존 `portfolio_id` 문자열은 `name` 컬럼으로 이전하고, `(owner_user_id, name)` 복합 UNIQUE 제약을 DB 레벨에서 도입해 사용자 범위 내 이름 유일성을 보장한다. CLI는 `` + `--user` 조합으로 portfolio를 식별하고 내부에서 `get_portfolio_by_name_and_user()`를 통해 UUID를 조회한다. HTTP API path parameter와 `GET /v1/assets` `asset_id` 반환은 UUID 기반으로 전환된다. ## Baseline vs Batch 변경점 ### docs/project-structure.md (현재 baseline) - `app/repositories/postgres.py`: SQL 세부사항 담당으로만 기술 — `get_portfolio_by_name_and_user()` 미기재 - `app/domain/models.py`: `portfolio_id` 필드 의미 변경 미기재 ### docs/runtime-flows.md (현재 baseline) - `CLI Operational Command Execution` flow: name+user 식별 방식 미기재 - `v1 API — GET /v1/assets` flow: `asset_id` 필드 언급 없음 ### docs/data-model.md (현재 baseline) - `portfolio_id :: text :: stable portfolio primary key` — UUID 의미 변경 미기재 - `name` 컬럼 별도 기재 없음 - `(owner_user_id, name)` UNIQUE 제약 미기재 ### docs/interface-contract.md (현재 baseline) - `GET /v1/assets` 응답 명세 없음 (bat-001에서 추가 예정이었으나 아직 반영 안 됨) - trex-basic 동시 배포 계획 없음 ## Changed Areas - Docs: - `docs/project-structure.md` - `docs/runtime-flows.md` - `docs/data-model.md` - `docs/interface-contract.md` - `docs/discovery/dcy-002_20260502_trex-basic-consumer-api-contract.md` (A-3 철회 주석) - Modules: - `migrations/versions/20260511_0015_portfolio_uuid_migration.py` (신규 Alembic revision) - `app/domain/models.py` (주석 없음, Portfolio dataclass 구조 변경 없음 — 기존 `portfolio_id` + `name` 필드 유지) - `app/repositories/postgres.py`: - `create_portfolio()`: `portfolio_id` 파라미터 제거, 서버 UUID 생성 - `get_portfolio_by_name_and_user()` 신규 메서드 - `ensure_schema()`: `(owner_user_id, name)` UNIQUE 제약 추가 - `update_portfolio()`: UniqueViolation 시 ValueError("이미 사용 중인 이름입니다") 반환 - `app/domain/services.py`: `PortfolioDomainRepository` Protocol 업데이트 - `app/schemas/portfolio.py`: - `CreatePortfolioRequest`: `portfolio_id` 필드 제거, `name: str` 필수로 변경, name 유효성 validator 추가 - `app/services/portfolio_service.py`: `create_portfolio()` 호출부 업데이트 - `app/cli/commands/portfolio.py`: - `create`: `portfolio_id` Argument → `name` Argument (필수), `--name` Option 제거 - `status`, `buy`, `sell`, `invest`, `transfer`, `reset`, `delete`: `portfolio_id` → `name` Argument + `--user` Option - `update`: `portfolio_id` → `name` Argument + `--user` Option + `--new-name` Option (이름 변경용) - `_resolve_portfolio_id()` helper 추가 - `_validate_portfolio_name()` helper 추가 - Interfaces: - CLI: `portfolio ` → `portfolio --user ` - HTTP API: `POST /portfolios` request body: `portfolio_id` 필드 제거, `name` 필수화 - HTTP API: `GET /v1/assets` `assets[].asset_id` → UUID 형식 반환 ## Key Decisions 1. **portfolio_id 컬럼 타입 TEXT 유지**: PostgreSQL UUID 타입 변환은 이번 범위 밖. `uuid.uuid4()` 문자열을 TEXT로 저장. 2. **get_portfolio_by_name_and_user() 공유**: CLI와 HTTP API 식별 모두 이 단일 repository 메서드 사용. Protocol에도 추가. 3. **이름 대소문자 구분**: `TV`와 `tv`는 다른 이름. DB UNIQUE 제약도 case-sensitive. 4. **단일 Alembic revision**: name 컬럼 추가 (IF NOT EXISTS), UUID 교체, FK 4개 테이블 갱신, UNIQUE 제약을 하나의 revision에서 수행. IF NOT EXISTS 가드로 재실행 안전성 보장. 5. **update 명령 name 변경**: CLI `update` 명령은 `--new-name` Option으로 이름 변경. `--force` 없으면 confirm 프롬프트. DB UNIQUE 위반 시 `이미 사용 중인 이름입니다: {name}` 오류. 6. **name 유효성**: `[a-zA-Z0-9._:-]`, 1~100자. Pydantic validator + CLI 레이어에서 이중 검사. 7. **comment/tag sub-commands**: REQ-017 명시 범위 밖. 이번 batch에서 변경하지 않음 (UUID 직접 전달 필요). 8. **dcy-002 A-3 공식 철회**: REQ-018 구현 시 해당 discovery 문서에 주석 추가. ## Edge Cases - **기존 portfolio_id 값이 UUID 형식인 경우**: migration이 이미 적용된 것으로 판단하는 guard (UNIQUE constraint 존재 여부로 판단). 재실행 시 skip. - **FK 고아 레코드**: migration revision 내 트랜잭션 후 orphan 검증 쿼리. 고아 발견 시 RuntimeError 발생. - **name 허용 문자 범위 외 입력**: CLI validation + Pydantic validator에서 조기 거부. - **동일 owner_user_id + 동일 name**: DB UNIQUE 위반 → `create_portfolio()`에서 ValueError, `update_portfolio()`에서 UniqueViolation catch → ValueError. - **존재하지 않는 name+user 조합**: `get_portfolio_by_name_and_user()` None 반환 → `포트폴리오를 찾을 수 없습니다: {name} (user: {username})`. - **revision 재실행**: `already_migrated` guard (UNIQUE constraint 유무 확인). 이미 적용됐으면 UUID 교체 블록 전체 skip. - **migration 중 portfolio_tags PK 업데이트**: PostgreSQL은 PK 값 UPDATE 허용. FK 제약 없으므로 안전. ## Architecture Impact both (project-structure + runtime-flows) ## Reference Doc Update Plan - project-structure.md: - `app/repositories/postgres.py` 항목: `get_portfolio_by_name_and_user(owner_user_id, name) → Portfolio | None` 추가 - `app/domain/models.py` 항목: `Portfolio.portfolio_id`가 서버 생성 UUID임을 명시 - runtime-flows.md: - `CLI Operational Command Execution` flow: `` + `--user` → `get_portfolio_by_name_and_user()` → UUID 조회 흐름 추가 - `v1 API — GET /v1/assets` flow: `asset_id = portfolio_id (UUID)` 반환 명시