chore: restructure SRS for asset catalog and tag model redesign (dcy-001/002, REQ-001~008)
Architecture Redesign (May 19, 2026): - Separate admin-managed asset catalog from user portfolios - Eliminate portfolio as asset definition; clarify portfolio as user position - Introduce new asset_catalog + portfolio structure Discovery Documents: - dcy-001: asset-catalog-and-user-portfolio-model — admin asset management and user portfolio separation - dcy-002: tag-ownership-and-applicability-policy — tag model implementation strategy New Requirements (REQ-001~008): - REQ-001: tags master table introduction (Data, Proposed) - REQ-002: assets.tag_ids UUID[] column addition (Data, Proposed) - REQ-003: portfolios.tag_ids and portfolio_tags removal (Data, Proposed) - REQ-004: tags master HTTP API (Interface, Proposed) - REQ-005: tag apply/remove HTTP API (Interface, Proposed) - REQ-006: portfolio_tags data migration (Migration, Proposed) - REQ-007: pfhub tag CLI commands (Interface, Proposed) - REQ-008: tag model baseline docs update (Documentation, Proposed) Cleanup: - Remove archived SRS documents (REQ-001~029 old structure) - Remove archived batch documentation (BAT-001~010) - Remove archived discovery documents (dcy-001~006 old structure) - Remove archived release documentation (REL-001~006) Data Model Updates: - Add Deployment category to SRS structure - Add Data category for new data model requirements - Update app/cli/core/utils.py for new tag handling Documentation: - Create new SRS index with consolidated requirement structure - Update docs/discovery/index.md with new discovery references - Migrate from requirement-centric to domain-model-centric organization Rationale: - Existing single-portfolio model lacks admin catalog governance - New asset catalog enables central management of tradeable symbols - Tag system supports user-driven asset organization and discovery - Cleaner separation of concerns between admin operations and user trading
This commit is contained in:
@@ -0,0 +1,74 @@
|
||||
# REQ-001: tags 마스터 테이블 도입
|
||||
|
||||
- Status: Proposed
|
||||
- Type: Data
|
||||
- Priority: High
|
||||
- Owner: Justin Ko
|
||||
|
||||
## Intent
|
||||
- 태그를 자유 문자열이 아닌 정규화된 마스터 테이블로 관리하여 소유권(created_by_user_id), admin 구분(is_admin_tag), 생명주기(is_active)를 추적할 수 있는 기반을 마련한다.
|
||||
- is_admin_tag는 태그 생성 시점의 권한을 스냅샷으로 기록하며 이후 권한 변경과 무관하게 고정된다.
|
||||
- 태그 삭제는 is_active = FALSE soft delete로만 수행하여 기존 참조가 archived 상태임을 확인할 수 있게 한다.
|
||||
|
||||
## Requirement
|
||||
- 시스템은 다음 스키마로 `tags` 테이블을 생성해야 한다: `(tag_id UUID PK DEFAULT gen_random_uuid(), name TEXT NOT NULL, created_by_user_id TEXT NOT NULL REFERENCES users(id), is_admin_tag BOOLEAN NOT NULL DEFAULT FALSE, is_active BOOLEAN NOT NULL DEFAULT TRUE, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()), UNIQUE(name)`
|
||||
- 태그 생성 시 요청자가 admin 권한(Permission.ADMIN)이면 is_admin_tag = TRUE로 기록하며 이후 이 값을 변경하지 않는다.
|
||||
- 태그 비활성화는 is_active = FALSE로만 수행하며 hard DELETE는 사용하지 않는다.
|
||||
|
||||
## Acceptance Criteria
|
||||
1. Given Alembic 마이그레이션이 적용된 DB, When `tags` 테이블 스키마를 조회, Then tag_id (UUID NOT NULL PK), name (TEXT NOT NULL UNIQUE), created_by_user_id (TEXT NOT NULL REFERENCES users.id), is_admin_tag (BOOLEAN NOT NULL DEFAULT FALSE), is_active (BOOLEAN NOT NULL DEFAULT TRUE), created_at (TIMESTAMPTZ NOT NULL DEFAULT NOW()) 컬럼이 모두 존재한다.
|
||||
2. Given admin 권한 토큰으로 태그 생성 요청, When 태그 행이 INSERT, Then is_admin_tag = TRUE이고 이후 권한이 변경되어도 tags.is_admin_tag는 변경되지 않는다.
|
||||
3. Given 일반(non-admin) 사용자 토큰으로 태그 생성 요청, When 태그 행이 INSERT, Then is_admin_tag = FALSE이다.
|
||||
4. Given 활성화된 태그, When 비활성화 요청, Then is_active = FALSE로 설정되며 해당 행은 DB에서 삭제되지 않는다.
|
||||
5. Given is_active = FALSE인 태그, When 태그를 조회, Then 행이 반환되며 비활성 상태를 확인할 수 있다.
|
||||
|
||||
## Impacted Area
|
||||
- Docs: docs/data-model.md (tags 테이블 추가)
|
||||
- Modules:
|
||||
- migrations/versions/ — tags 테이블 생성 Alembic revision
|
||||
- app/domain/models.py — Tag 도메인 모델 추가
|
||||
- app/repositories/portfolio_repository.py — tags CRUD 메서드 프로토콜 추가
|
||||
- app/repositories/postgres.py — tags INSERT/SELECT/UPDATE SQL 구현
|
||||
- Tests: tags 테이블 스키마 검증, is_admin_tag 스냅샷 동작 검증
|
||||
|
||||
## Notes
|
||||
- created_by_user_id는 REFERENCES users(id)로 외래키를 가진다. users 테이블은 기존에 존재한다 (migration 0004).
|
||||
- is_admin_tag 판단은 런타임 JOIN 없이 tags 테이블 컬럼만으로 수행된다 (dcy-002 A-1).
|
||||
- name UNIQUE 제약은 TEXT 수준에서 적용되며, 소문자 정규화는 애플리케이션 레이어(REQ-004)에서 수행한다.
|
||||
- Source: dcy-002 FR-1, NFR-1(부분), OQ-4
|
||||
|
||||
## Change Log
|
||||
|
||||
### CHG-20260519-01
|
||||
- Date: 2026-05-19
|
||||
- Author: Justin Ko
|
||||
|
||||
#### Change Summary
|
||||
- 최초 생성 — dcy-002 confirm-discovery 후 REQ 초안 작성
|
||||
|
||||
#### Intent
|
||||
- dcy-002 FR-1을 구현 가능한 REQ 단위로 정규화
|
||||
|
||||
#### Acceptance Criteria Delta
|
||||
- Added:
|
||||
- AC-1~5 최초 작성
|
||||
|
||||
#### Impacted Area Delta
|
||||
- Docs:
|
||||
- docs/data-model.md — tags 테이블 추가
|
||||
- Modules:
|
||||
- migrations/versions/, app/domain/models.py, app/repositories/ — 최초 지정
|
||||
- Tests:
|
||||
- 최초 지정
|
||||
|
||||
#### Delivery Trace Delta
|
||||
- Batches:
|
||||
- none (미정)
|
||||
- Releases:
|
||||
- none (미정)
|
||||
|
||||
#### Revalidation Impact
|
||||
- Reverification Needed: no
|
||||
- Existing Implementation Invalidated: no
|
||||
- Status Recommendation: keep-current
|
||||
- Follow-up Action: confirm-req 후 batch 계획 편입
|
||||
@@ -0,0 +1,73 @@
|
||||
# REQ-002: assets.tag_ids UUID[] 컬럼 추가
|
||||
|
||||
- Status: Proposed
|
||||
- Type: Data
|
||||
- Priority: High
|
||||
- Owner: Justin Ko
|
||||
|
||||
## Intent
|
||||
- assets(portfolios) 테이블에 UUID[] 배열 컬럼을 추가하여 junction 테이블 없이 tags 마스터 테이블 기반 태그 참조를 저장한다.
|
||||
- GIN 인덱스로 배열 조회 성능을 확보하고, 비활성화된 태그는 조용히 제거하지 않고 archived 상태로 반환하여 이력 추적을 가능하게 한다.
|
||||
- 태그 적용 정책 검증(is_admin_tag=TRUE AND is_active=TRUE 확인, 403 반환)은 REQ-005에서 구현한다.
|
||||
|
||||
## Requirement
|
||||
- portfolios 테이블(asset에 해당)에 `tag_ids UUID[] NOT NULL DEFAULT '{}'` 컬럼을 추가해야 한다.
|
||||
- portfolios.tag_ids에 GIN 인덱스를 생성해야 한다.
|
||||
- tag_ids 배열에 is_active = FALSE인 tag_id가 포함된 경우, 해당 항목을 조용히 제거하지 않고 archived 상태임을 명시하여 반환해야 한다.
|
||||
|
||||
## Acceptance Criteria
|
||||
1. Given Alembic 마이그레이션이 적용된 DB, When portfolios 테이블 스키마를 조회, Then tag_ids UUID[] NOT NULL DEFAULT '{}' 컬럼이 존재한다.
|
||||
2. Given 마이그레이션이 적용된 DB, When portfolios.tag_ids 인덱스를 조회, Then GIN 인덱스가 존재한다.
|
||||
3. Given asset의 tag_ids 배열에 is_active = FALSE인 tag_id가 포함, When 해당 asset의 태그 목록을 조회, Then 비활성 태그가 archived 표시와 함께 반환되며 배열에서 자동 제거되지 않는다.
|
||||
4. Given tag_ids = '{}' 기본값인 asset, When 태그가 적용되지 않은 상태로 조회, Then 빈 배열이 반환된다.
|
||||
|
||||
## Impacted Area
|
||||
- Docs: docs/data-model.md (portfolios 스키마에 tag_ids 컬럼 추가)
|
||||
- Modules:
|
||||
- migrations/versions/ — portfolios.tag_ids 컬럼 추가 및 GIN 인덱스 생성 Alembic revision
|
||||
- app/domain/models.py — Portfolio 도메인 모델에 tag_ids 필드 추가
|
||||
- app/repositories/postgres.py — tag_ids 배열 조회 시 archived 표시 로직
|
||||
- app/schemas/v1.py — AssetItem에 tag_ids 필드 추가
|
||||
- Tests: portfolios 테이블 스키마 검증, GIN 인덱스 존재 확인, archived 표시 동작 확인
|
||||
|
||||
## Notes
|
||||
- dcy-001의 assets 테이블(portfolios)이 확정된 이후 이 REQ를 구현해야 한다 (dcy-002 R-2).
|
||||
- 배열 참조 무결성은 애플리케이션 레이어에서 보장한다 (dcy-002 NFR-2). PostgreSQL ARRAY FK는 미사용.
|
||||
- portfolios 테이블이 v1 API의 "asset" 개념에 해당한다 (portfolio_id = asset_id in v1 API).
|
||||
- Source: dcy-002 FR-2, NFR-2
|
||||
|
||||
## Change Log
|
||||
|
||||
### CHG-20260519-01
|
||||
- Date: 2026-05-19
|
||||
- Author: Justin Ko
|
||||
|
||||
#### Change Summary
|
||||
- 최초 생성 — dcy-002 confirm-discovery 후 REQ 초안 작성
|
||||
|
||||
#### Intent
|
||||
- dcy-002 FR-2의 스키마 변경 부분을 독립 구현 단위로 분리
|
||||
|
||||
#### Acceptance Criteria Delta
|
||||
- Added:
|
||||
- AC-1~4 최초 작성
|
||||
|
||||
#### Impacted Area Delta
|
||||
- Docs:
|
||||
- docs/data-model.md — tag_ids 컬럼 추가
|
||||
- Modules:
|
||||
- migrations/versions/, app/domain/models.py, app/repositories/postgres.py, app/schemas/v1.py — 최초 지정
|
||||
- Tests:
|
||||
- 최초 지정
|
||||
|
||||
#### Delivery Trace Delta
|
||||
- Batches:
|
||||
- none (미정)
|
||||
- Releases:
|
||||
- none (미정)
|
||||
|
||||
#### Revalidation Impact
|
||||
- Reverification Needed: no
|
||||
- Existing Implementation Invalidated: no
|
||||
- Status Recommendation: keep-current
|
||||
- Follow-up Action: confirm-req 후 batch 계획 편입
|
||||
@@ -0,0 +1,73 @@
|
||||
# REQ-003: portfolios.tag_ids UUID[] 컬럼 추가 및 portfolio_tags 테이블 제거
|
||||
|
||||
- Status: Proposed
|
||||
- Type: Data
|
||||
- Priority: High
|
||||
- Owner: Justin Ko
|
||||
|
||||
## Intent
|
||||
- portfolios 테이블에 UUID[] 배열 컬럼을 추가하고 기존 자유 문자열 기반 portfolio_tags 테이블을 제거하여 tags 마스터 테이블 기반 태그 참조 구조로 전환한다.
|
||||
- 이 REQ는 스키마 변경에만 집중하며, 기존 portfolio_tags 데이터 이전 절차는 REQ-006에서 다룬다.
|
||||
- GIN 인덱스로 배열 조회 성능을 확보하고, 비활성 태그는 archived로 표시한다.
|
||||
|
||||
## Requirement
|
||||
- portfolios 테이블에 `tag_ids UUID[] NOT NULL DEFAULT '{}'` 컬럼을 추가해야 한다.
|
||||
- portfolios.tag_ids에 GIN 인덱스를 생성해야 한다.
|
||||
- REQ-006 데이터 마이그레이션 완료 후 portfolio_tags 테이블을 DROP해야 한다.
|
||||
- portfolios.tag_ids에 is_active = FALSE인 tag_id가 포함된 경우, archived 상태임을 명시하여 반환해야 하며 자동 제거하지 않는다.
|
||||
|
||||
## Acceptance Criteria
|
||||
1. Given Alembic 마이그레이션(REQ-006 포함)이 적용된 DB, When portfolios 테이블 스키마를 조회, Then tag_ids UUID[] NOT NULL DEFAULT '{}' 컬럼이 존재한다.
|
||||
2. Given 마이그레이션이 적용된 DB, When portfolios.tag_ids 인덱스를 조회, Then GIN 인덱스가 존재한다.
|
||||
3. Given 마이그레이션이 적용된 DB, When DB 스키마를 조회, Then portfolio_tags 테이블이 존재하지 않는다.
|
||||
4. Given portfolio의 tag_ids 배열에 is_active = FALSE인 tag_id가 포함, When 해당 portfolio의 태그 목록을 조회, Then 비활성 태그가 archived 표시와 함께 반환되며 배열에서 자동 제거되지 않는다.
|
||||
|
||||
## Impacted Area
|
||||
- Docs: docs/data-model.md (portfolios 스키마에 tag_ids 추가, portfolio_tags 테이블 제거 반영)
|
||||
- Modules:
|
||||
- migrations/versions/ — portfolios.tag_ids 컬럼 추가, GIN 인덱스 생성, portfolio_tags DROP Alembic revision
|
||||
- app/domain/models.py — Portfolio 도메인 모델에 tag_ids 필드 추가
|
||||
- app/repositories/postgres.py — portfolio tag_ids 배열 조작 메서드, archived 표시 로직
|
||||
- app/schemas/ — PortfolioItem에 tag_ids 필드 추가 (해당 스키마 존재 시)
|
||||
- Tests: portfolios 테이블 스키마 검증, portfolio_tags 테이블 부재 확인, GIN 인덱스 존재 확인, archived 표시 동작
|
||||
|
||||
## Notes
|
||||
- REQ-006(portfolio_tags 데이터 마이그레이션)이 선행되어야 한다. portfolio_tags DROP은 데이터 마이그레이션 완료 후 수행.
|
||||
- 기존 `pfhub portfolio tag add/list/remove` 명령어는 portfolio_tags 테이블에 의존하므로 이 REQ와 함께 비활성화/제거한다 (REQ-007 참조).
|
||||
- Source: dcy-002 FR-3, NFR-2
|
||||
|
||||
## Change Log
|
||||
|
||||
### CHG-20260519-01
|
||||
- Date: 2026-05-19
|
||||
- Author: Justin Ko
|
||||
|
||||
#### Change Summary
|
||||
- 최초 생성 — dcy-002 confirm-discovery 후 REQ 초안 작성
|
||||
|
||||
#### Intent
|
||||
- dcy-002 FR-3의 스키마 변경을 독립 REQ 단위로 분리
|
||||
|
||||
#### Acceptance Criteria Delta
|
||||
- Added:
|
||||
- AC-1~4 최초 작성
|
||||
|
||||
#### Impacted Area Delta
|
||||
- Docs:
|
||||
- docs/data-model.md — portfolio_tags 제거, tag_ids 추가
|
||||
- Modules:
|
||||
- migrations/versions/, app/domain/models.py, app/repositories/postgres.py, app/schemas/ — 최초 지정
|
||||
- Tests:
|
||||
- 최초 지정
|
||||
|
||||
#### Delivery Trace Delta
|
||||
- Batches:
|
||||
- none (미정)
|
||||
- Releases:
|
||||
- none (미정)
|
||||
|
||||
#### Revalidation Impact
|
||||
- Reverification Needed: no
|
||||
- Existing Implementation Invalidated: no
|
||||
- Status Recommendation: keep-current
|
||||
- Follow-up Action: confirm-req 후 batch 계획 편입; REQ-006 선행 의존성 확인
|
||||
Reference in New Issue
Block a user