build: /spec-feature data-models --requirements 'Define Task, Project, Label...
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
This commit is contained in:
parent
b7ee567d80
commit
9c9e07706f
@ -22,7 +22,7 @@ artifacts:
|
||||
status: pending
|
||||
path: review.md
|
||||
spec:
|
||||
status: pending
|
||||
status: draft
|
||||
path: spec.md
|
||||
tasks:
|
||||
status: pending
|
||||
|
||||
124
.sdlc/features/data-models/spec.md
Normal file
124
.sdlc/features/data-models/spec.md
Normal file
@ -0,0 +1,124 @@
|
||||
# Feature: Core Data Models & Persistence
|
||||
|
||||
## Problem Statement
|
||||
|
||||
The studio-api service currently uses in-memory storage (via `adapter/memory`) with a single `Example` entity. To support real product development workflows, the service needs persistent, relational data models for managing **Projects**, **Tasks**, **Labels**, and **Assignments**. Without these, the application cannot track work items, organize them into projects, categorize with labels, or assign work to users.
|
||||
|
||||
These entities form the backbone of a project management domain and must be stored in PostgreSQL via the existing `pkg/database` infrastructure.
|
||||
|
||||
## User Stories
|
||||
|
||||
- As a **user**, I want to create and manage **projects** so that I can organize related work items together.
|
||||
- As a **user**, I want to create, update, and delete **tasks** within a project so that I can track individual work items.
|
||||
- As a **user**, I want to create and manage **labels** so that I can categorize and filter tasks.
|
||||
- As a **user**, I want to attach labels to tasks so that tasks can be categorized across multiple dimensions.
|
||||
- As a **user**, I want to assign tasks to users so that ownership and responsibility are clear.
|
||||
- As a **user**, I want to list tasks with filtering (by project, label, assignee, status) so that I can find relevant work items quickly.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
### Projects
|
||||
- [ ] Project entity with fields: id (UUID), name, description, created_at, updated_at
|
||||
- [ ] Full CRUD endpoints: `POST /projects`, `GET /projects`, `GET /projects/{id}`, `PUT /projects/{id}`, `DELETE /projects/{id}`
|
||||
- [ ] Project name is required (1-100 chars), description optional (max 500 chars)
|
||||
- [ ] Deleting a project cascades to its tasks, task-label associations, and assignments
|
||||
- [ ] Duplicate project names return 409 Conflict
|
||||
|
||||
### Tasks
|
||||
- [ ] Task entity with fields: id (UUID), project_id (FK), title, description, status, priority, created_at, updated_at
|
||||
- [ ] Status enum: `open`, `in_progress`, `done`, `cancelled`
|
||||
- [ ] Priority enum: `low`, `medium`, `high`, `critical`
|
||||
- [ ] Full CRUD endpoints: `POST /projects/{projectId}/tasks`, `GET /projects/{projectId}/tasks`, `GET /tasks/{id}`, `PUT /tasks/{id}`, `DELETE /tasks/{id}`
|
||||
- [ ] Task title is required (1-200 chars), description optional (max 2000 chars)
|
||||
- [ ] Tasks belong to exactly one project (project_id required, validated)
|
||||
- [ ] Creating a task for a non-existent project returns 404
|
||||
- [ ] List tasks supports filtering by status and priority via query parameters
|
||||
|
||||
### Labels
|
||||
- [ ] Label entity with fields: id (UUID), name, color, created_at
|
||||
- [ ] Full CRUD endpoints: `POST /labels`, `GET /labels`, `GET /labels/{id}`, `PUT /labels/{id}`, `DELETE /labels/{id}`
|
||||
- [ ] Label name is required (1-50 chars), unique; color is optional (hex format, e.g. `#FF5733`)
|
||||
- [ ] Deleting a label removes its task-label associations (cascade)
|
||||
- [ ] Duplicate label names return 409 Conflict
|
||||
|
||||
### Assignments (Task-Label & Task-User associations)
|
||||
- [ ] Task-Label join: `POST /tasks/{taskId}/labels/{labelId}`, `DELETE /tasks/{taskId}/labels/{labelId}`, `GET /tasks/{taskId}/labels`
|
||||
- [ ] Task-User assignment: `POST /tasks/{taskId}/assignments`, `DELETE /tasks/{taskId}/assignments/{assignmentId}`, `GET /tasks/{taskId}/assignments`
|
||||
- [ ] Assignment entity with fields: id (UUID), task_id (FK), user_id (string), assigned_at
|
||||
- [ ] Assigning a non-existent label or task returns 404
|
||||
- [ ] Duplicate task-label associations return 409 Conflict
|
||||
- [ ] Duplicate task-user assignments return 409 Conflict
|
||||
|
||||
### Database & Migrations
|
||||
- [ ] PostgreSQL migrations in `services/studio-api/migrations/` using `pkg/database.RunMigrations`
|
||||
- [ ] Migration files follow `NNN_description.sql` naming convention
|
||||
- [ ] All tables use UUID primary keys
|
||||
- [ ] Foreign keys with appropriate ON DELETE CASCADE constraints
|
||||
- [ ] Indexes on foreign key columns and commonly queried fields (status, priority)
|
||||
- [ ] Schema supports the `sqlx` struct scanning pattern (snake_case column names)
|
||||
|
||||
### Architecture & Code Quality
|
||||
- [ ] Domain entities in `internal/domain/` with constructor validation (follows `Example` pattern)
|
||||
- [ ] Strong-typed IDs (`ProjectID`, `TaskID`, `LabelID`, `AssignmentID`) following `ExampleID` pattern
|
||||
- [ ] Domain errors in `internal/domain/errors.go` for each entity (NotFound, Duplicate, Invalid)
|
||||
- [ ] Port interfaces in `internal/port/` defining repository contracts
|
||||
- [ ] PostgreSQL adapter implementations in `internal/adapter/postgres/`
|
||||
- [ ] Service layer in `internal/service/` with business logic and validation
|
||||
- [ ] HTTP handlers in `internal/api/handlers/` following `app.Wrap()` pattern
|
||||
- [ ] Routes registered in `internal/api/routes.go` under `/api/studio-api/`
|
||||
- [ ] OpenAPI spec updated in `internal/api/spec.go`
|
||||
- [ ] `main.go` updated to wire PostgreSQL pool and new services
|
||||
|
||||
### Testing
|
||||
- [ ] Unit tests for each service layer (mock repositories, follows `example_test.go` pattern)
|
||||
- [ ] Unit tests for each handler (httptest, chi router, follows `example_test.go` pattern)
|
||||
- [ ] Domain entity constructor and validation tests
|
||||
- [ ] All tests pass with `cd services/studio-api && go test -v ./...`
|
||||
|
||||
### Response Format
|
||||
- [ ] All endpoints use `httpresponse.OK/Created/NoContent` envelope pattern
|
||||
- [ ] All errors use `httperror.BadRequest/NotFound/Conflict` typed errors
|
||||
- [ ] Request bodies validated with `app.BindAndValidate()`
|
||||
- [ ] URL parameters use `{param}` brace syntax (not colon syntax)
|
||||
|
||||
## Technical Constraints
|
||||
|
||||
- **Database**: PostgreSQL via `pkg/database` (sqlx-based, `*sqlx.DB`)
|
||||
- **Migrations**: Embedded SQL files via `//go:embed`, run at startup with `database.RunMigrations`
|
||||
- **IDs**: UUID v4, generated server-side (use `github.com/google/uuid`)
|
||||
- **JSON serialization**: Standard `encoding/json` with `json:"field_name"` tags
|
||||
- **Validation**: `app.BindAndValidate()` for request bodies, domain-level validation for business rules
|
||||
- **Router**: Chi with `{param}` brace syntax for URL parameters
|
||||
- **No ORM**: Use raw SQL with sqlx (`Get`, `Select`, `NamedExec`, `ExecContext`)
|
||||
- **Transactions**: Use `database.WithTx` for multi-table mutations (e.g., cascade deletes if not handled by DB constraints)
|
||||
|
||||
## Dependencies
|
||||
|
||||
- `pkg/database` - PostgreSQL connection pool and migration runner (exists)
|
||||
- `pkg/app` - Service bootstrapper, `Wrap`, `Bind`, `BindAndValidate` (exists)
|
||||
- `pkg/httperror` - Typed HTTP errors (exists)
|
||||
- `pkg/httpresponse` - Response envelope helpers (exists)
|
||||
- `pkg/logging` - Structured logging (exists)
|
||||
- `pkg/auth` - JWT middleware for protected routes (exists)
|
||||
- `pkg/openapi` - OpenAPI spec builder (exists)
|
||||
- `github.com/google/uuid` - UUID generation (add to go.mod)
|
||||
- PostgreSQL instance accessible via `DATABASE_URL` environment variable
|
||||
|
||||
## Out of Scope
|
||||
|
||||
- User management / user CRUD (user_id in assignments is an opaque string, not a managed entity)
|
||||
- File attachments on tasks
|
||||
- Task comments or activity history
|
||||
- Real-time notifications (WebSocket updates for task changes)
|
||||
- Pagination (can be added later; initial list endpoints return all matching records)
|
||||
- Full-text search on task titles/descriptions
|
||||
- Soft deletes (hard deletes for simplicity; soft deletes can be added later)
|
||||
- Removing the existing `Example` entity and its in-memory adapter (keep for reference)
|
||||
|
||||
## Open Questions
|
||||
|
||||
1. **User identity format**: Should `user_id` in assignments be a UUID (anticipating a future users table) or a free-form string (e.g., email or external auth ID)? *Spec assumes string for flexibility.*
|
||||
2. **Task ordering**: Should tasks have an explicit `position`/`sort_order` field for manual reordering within a project, or is ordering by `created_at` sufficient for v1?
|
||||
3. **Label scoping**: Should labels be global (shared across all projects) or scoped per-project? *Spec assumes global labels.*
|
||||
4. **Cascade behavior**: When a project is deleted, should its tasks be deleted (cascade) or should deletion be blocked if tasks exist? *Spec assumes cascade delete via FK constraint.*
|
||||
5. **Auth on reads**: Should `GET` (list/detail) endpoints be public or require authentication? *Current `Example` pattern has reads as public, writes as protected. Spec follows this pattern.*
|
||||
Loading…
Reference in New Issue
Block a user