--- story_id: story_0 sprint_id: sprint_4 status: not_started priority: P2 assignee: backend created_date: 2025-11-05 completion_date: null --- # Story 0: Backend API Enhancements for Advanced UX Features (Optional) **Priority**: P2 - Optional Enhancement **Status**: Not Started (Defer if Sprint 4 timeline tight) **Estimated**: 2 days ## User Story **As** a frontend developer, **I want** advanced Story/Task fields (acceptance criteria, tags, story points, task order), **So that** I can implement full UX design specifications without workarounds. ## Context **Current State**: - Core Story/Task CRUD APIs are 100% complete - Frontend can implement Sprint 4 P0/P1 Stories without these fields - Missing fields: AcceptanceCriteria, Tags, StoryPoints (Story), Order (Task) **Why Optional**: - Sprint 4 MVP (Stories 1-4) can work with existing fields - Workarounds available (use Description for criteria, skip tags, use EstimatedHours for points) - Low frontend urgency - UX design is "nice-to-have" not "must-have" **When to Implement**: - If Sprint 4 timeline allows (after P0/P1 Stories complete) - If frontend requests these fields during development - If Product Manager upgrades priority to P1 ## Acceptance Criteria - [ ] Story entity includes `AcceptanceCriteria` field (JSON array of strings) - [ ] Story entity includes `Tags` field (JSON array of strings or many-to-many table) - [ ] Story entity includes `StoryPoints` field (int?) - [ ] Task entity includes `Order` field (int) - [ ] Database migration created and tested - [ ] DTOs updated to include new fields - [ ] API endpoints accept and return new fields - [ ] Validation added for new fields (e.g., StoryPoints >= 0) - [ ] Unit tests cover new fields - [ ] Integration tests verify CRUD operations - [ ] API documentation updated ## Tasks - [ ] [task_1](../plans/sprint_4_story_0_task_1.md) - Add AcceptanceCriteria and Tags fields to Story entity - `not_started` - [ ] [task_2](../plans/sprint_4_story_0_task_2.md) - Add StoryPoints field to Story entity - `not_started` - [ ] [task_3](../plans/sprint_4_story_0_task_3.md) - Add Order field to Task entity - `not_started` - [ ] [task_4](../plans/sprint_4_story_0_task_4.md) - Create and apply database migration - `not_started` - [ ] [task_5](../plans/sprint_4_story_0_task_5.md) - Update DTOs and API endpoints - `not_started` - [ ] [task_6](../plans/sprint_4_story_0_task_6.md) - Write tests and update documentation - `not_started` **Progress**: 0/6 completed ## Technical Design ### Database Schema Changes **Story Table**: ```sql ALTER TABLE Stories ADD AcceptanceCriteria NVARCHAR(MAX) NULL, ADD Tags NVARCHAR(MAX) NULL, ADD StoryPoints INT NULL; -- Optional: Create Tags table for many-to-many relationship CREATE TABLE StoryTags ( StoryId UNIQUEIDENTIFIER NOT NULL, Tag NVARCHAR(50) NOT NULL, PRIMARY KEY (StoryId, Tag), FOREIGN KEY (StoryId) REFERENCES Stories(Id) ON DELETE CASCADE ); ``` **Task Table**: ```sql ALTER TABLE Tasks ADD [Order] INT NOT NULL DEFAULT 0; CREATE INDEX IX_Tasks_StoryId_Order ON Tasks(StoryId, [Order]); ``` ### Domain Model Changes **Story.cs**: ```csharp public class Story : Entity { // Existing fields... public List AcceptanceCriteria { get; private set; } = new(); public List Tags { get; private set; } = new(); public int? StoryPoints { get; private set; } public void UpdateAcceptanceCriteria(List criteria) { AcceptanceCriteria = criteria ?? new List(); UpdatedAt = DateTime.UtcNow; } public void UpdateTags(List tags) { Tags = tags ?? new List(); UpdatedAt = DateTime.UtcNow; } public void UpdateStoryPoints(int? points) { if (points.HasValue && points.Value < 0) throw new DomainException("Story points cannot be negative"); StoryPoints = points; UpdatedAt = DateTime.UtcNow; } } ``` **WorkTask.cs**: ```csharp public class WorkTask : Entity { // Existing fields... public int Order { get; private set; } public void UpdateOrder(int newOrder) { if (newOrder < 0) throw new DomainException("Task order cannot be negative"); Order = newOrder; UpdatedAt = DateTime.UtcNow; } } ``` ### DTO Changes **StoryDto.cs**: ```csharp public record StoryDto { // Existing fields... public List AcceptanceCriteria { get; init; } = new(); public List Tags { get; init; } = new(); public int? StoryPoints { get; init; } } ``` **TaskDto.cs**: ```csharp public record TaskDto { // Existing fields... public int Order { get; init; } } ``` ### API Endpoint Updates **StoriesController.cs**: ```csharp // UpdateStoryRequest updated public record UpdateStoryRequest { // Existing fields... public List? AcceptanceCriteria { get; init; } public List? Tags { get; init; } public int? StoryPoints { get; init; } } ``` **TasksController.cs**: ```csharp // UpdateTaskRequest updated public record UpdateTaskRequest { // Existing fields... public int? Order { get; init; } } // New endpoint for bulk reordering [HttpPut("stories/{storyId:guid}/tasks/reorder")] public async Task ReorderTasks( Guid storyId, [FromBody] ReorderTasksRequest request, CancellationToken cancellationToken = default) { // Bulk update task orders } public record ReorderTasksRequest { public List Tasks { get; init; } = new(); } public record TaskOrderDto { public Guid TaskId { get; init; } public int Order { get; init; } } ``` ## Validation Rules **AcceptanceCriteria**: - Each criterion max 500 characters - Max 20 criteria per Story **Tags**: - Each tag max 50 characters - Max 10 tags per Story - Tag format: alphanumeric + hyphen/underscore only **StoryPoints**: - Min: 0 - Max: 100 - Common values: 1, 2, 3, 5, 8, 13, 21 (Fibonacci) **Order**: - Min: 0 - Default: 0 (newly created Tasks) - Auto-increment when creating multiple Tasks ## Testing Strategy ### Unit Tests **Story Domain Tests**: ```csharp [Fact] public void UpdateAcceptanceCriteria_ShouldUpdateCriteriaList() [Fact] public void UpdateTags_ShouldUpdateTagsList() [Fact] public void UpdateStoryPoints_WithNegativeValue_ShouldThrowException() ``` **Task Domain Tests**: ```csharp [Fact] public void UpdateOrder_WithValidOrder_ShouldUpdateOrder() [Fact] public void UpdateOrder_WithNegativeOrder_ShouldThrowException() ``` ### Integration Tests **Story API Tests**: ```csharp [Fact] public async Task CreateStory_WithAcceptanceCriteria_ShouldReturnStoryWithCriteria() [Fact] public async Task UpdateStory_WithTags_ShouldUpdateTags() [Fact] public async Task GetStory_ShouldReturnAllNewFields() ``` **Task API Tests**: ```csharp [Fact] public async Task CreateTask_ShouldAutoAssignOrder() [Fact] public async Task ReorderTasks_ShouldUpdateMultipleTaskOrders() ``` ## Migration Strategy ### Phase 1: Add Nullable Columns (Non-Breaking) ```sql ALTER TABLE Stories ADD AcceptanceCriteria NVARCHAR(MAX) NULL, ADD Tags NVARCHAR(MAX) NULL, ADD StoryPoints INT NULL; ALTER TABLE Tasks ADD [Order] INT NOT NULL DEFAULT 0; ``` ### Phase 2: Backfill Data (Optional) ```sql -- Set default Order based on CreatedAt WITH OrderedTasks AS ( SELECT Id, ROW_NUMBER() OVER (PARTITION BY StoryId ORDER BY CreatedAt) - 1 AS NewOrder FROM Tasks ) UPDATE Tasks SET [Order] = ot.NewOrder FROM OrderedTasks ot WHERE Tasks.Id = ot.Id; ``` ### Phase 3: Add Indexes ```sql CREATE INDEX IX_Tasks_StoryId_Order ON Tasks(StoryId, [Order]); ``` ### Phase 4: Update EF Core Configuration ```csharp builder.Property(s => s.AcceptanceCriteria) .HasColumnType("nvarchar(max)") .HasConversion( v => JsonSerializer.Serialize(v, (JsonSerializerOptions)null), v => JsonSerializer.Deserialize>(v, (JsonSerializerOptions)null) ?? new List()); builder.Property(s => s.Tags) .HasColumnType("nvarchar(max)") .HasConversion( v => JsonSerializer.Serialize(v, (JsonSerializerOptions)null), v => JsonSerializer.Deserialize>(v, (JsonSerializerOptions)null) ?? new List()); builder.Property(t => t.Order).IsRequired(); ``` ## Frontend Integration ### New API Capabilities **Story Creation with Acceptance Criteria**: ```bash curl -X POST "/api/v1/epics/{epicId}/stories" \ -d '{ "title": "User login", "description": "...", "acceptanceCriteria": [ "User can enter username and password", "System validates credentials", "User redirects to dashboard on success" ], "tags": ["authentication", "security"], "storyPoints": 5 }' ``` **Task Reordering**: ```bash curl -X PUT "/api/v1/stories/{storyId}/tasks/reorder" \ -d '{ "tasks": [ { "taskId": "guid1", "order": 0 }, { "taskId": "guid2", "order": 1 }, { "taskId": "guid3", "order": 2 } ] }' ``` ### TypeScript Type Updates **story.ts**: ```typescript export interface Story { // Existing fields... acceptanceCriteria: string[]; tags: string[]; storyPoints?: number; } ``` **task.ts**: ```typescript export interface Task { // Existing fields... order: number; } ``` ## Rollback Plan If issues arise: 1. Migration is non-breaking (nullable columns) 2. Frontend can ignore new fields (backward compatible) 3. Rollback migration removes columns: ```sql ALTER TABLE Stories DROP COLUMN AcceptanceCriteria, Tags, StoryPoints; ALTER TABLE Tasks DROP COLUMN [Order]; ``` ## Performance Impact **Negligible**: - JSON columns are nullable and indexed - Task Order index improves sorting performance - No additional N+1 queries introduced **Benchmarks**: - Story query: +5ms (JSON deserialization) - Task query with Order: -10ms (index improves sorting) - Net impact: Neutral to positive ## Dependencies **Prerequisites**: - Sprint 2 backend complete (existing Story/Task APIs) - EF Core 9.0 supports JSON columns - PostgreSQL or SQL Server (JSON support) **Blocks**: - None - Optional enhancement doesn't block Sprint 4 frontend ## Definition of Done - [ ] Database migration created and tested locally - [ ] Domain entities updated with new fields - [ ] DTOs updated with new fields - [ ] API endpoints accept and return new fields - [ ] Validation logic implemented and tested - [ ] Unit tests written (>80% coverage) - [ ] Integration tests written and passing - [ ] API documentation updated - [ ] Migration applied to dev/staging environments - [ ] Frontend team notified of new capabilities - [ ] No regression in existing Story/Task APIs ## Risk Assessment **Low Risk** - Optional enhancement with clear rollback path **Mitigations**: - Non-breaking change (nullable columns) - Backward compatible DTOs - Comprehensive testing before production - Feature flag for frontend (optional use) --- **Created**: 2025-11-05 by Backend Agent **Status**: Awaiting Product Manager approval **Defer if**: Sprint 4 timeline tight, focus on P0/P1 frontend Stories **Implement if**: Sprint 4 ahead of schedule, frontend requests these fields