Files
ColaFlow/docs/architecture/ADR-035-EPIC-STORY-TASK-ARCHITECTURE.md
Yaojia Wang 08b317e789
Some checks failed
Code Coverage / Generate Coverage Report (push) Has been cancelled
Tests / Run Tests (9.0.x) (push) Has been cancelled
Tests / Docker Build Test (push) Has been cancelled
Tests / Test Summary (push) Has been cancelled
Add trace files.
2025-11-04 23:28:56 +01:00

18 KiB

ADR-035: Epic/Story/Task Architecture Decision

Date: 2025-11-04 Status: Accepted Decision Maker: Product Manager + Architect Context: M1 Architecture Clarification


Context and Problem Statement

During Day 14 review, we discovered two different implementations for task management:

Implementation 1: ProjectManagement Module (Exists but Incomplete)

Location: colaflow-api/src/Modules/ProjectManagement/

Structure:

Project
  └─ Epic
      └─ Story
          └─ WorkTask

Status:

  • Partial implementation exists
  • Has Epic/Story/Task CRUD commands
  • Has API controllers (EpicsController)
  • No multi-tenant isolation verification
  • No integration tests
  • Not used by frontend

Implementation 2: Issue Management Module (Day 13, Complete & Production-Ready)

Location: colaflow-api/src/Modules/IssueManagement/

Structure:

Issue (type: Story | Task | Bug | Epic)
  - No parent-child hierarchy (flat structure)
  - Type is just an enum property

Status:

  • Complete implementation (59 files, 1630 lines)
  • CQRS + DDD architecture
  • Multi-tenant isolation: 100% verified (Day 14 CRITICAL fix)
  • Integration tests: 8/8 passing
  • Frontend integrated (Kanban board working)
  • Production-ready

Problem: Which architecture should we use? Do we need to migrate or integrate?


Decision

Core Decision: Use Issue Management Module as Foundation

We choose Implementation 2 (Issue Management Module) as the primary architecture.

Rationale:

  1. Production-Ready: Fully tested, multi-tenant secured, frontend integrated
  2. Clean Architecture: CQRS + DDD, proven architecture pattern
  3. Zero Migration Risk: Already working in production
  4. Time-Efficient: No need to rewrite existing functionality
  5. Extensible: Easy to add parent-child hierarchy as enhancement

Architecture Decision

Phase 1: Keep Issue Management Module (Current State)

Timeline: Already Complete (Day 13-14)

What We Have:

  • Issue entity with IssueType enum (Story, Task, Bug, Epic)
  • Full CRUD operations
  • Kanban board integration
  • Multi-tenant isolation
  • Real-time updates (SignalR)
  • Performance optimized (< 5ms queries)

Status: KEEP AS-IS

Phase 2: Add Parent-Child Hierarchy (M1 Requirement)

Timeline: Day 15-17 (2-3 days)

What to Add:

// Add to Issue entity
public class Issue : TenantEntity, IAggregateRoot
{
    // Existing properties...

    // NEW: Hierarchy support
    public Guid? ParentIssueId { get; private set; }
    public Issue? ParentIssue { get; private set; }
    public List<Issue> ChildIssues { get; private set; } = new();

    // Hierarchy rules
    public void SetParent(Issue parent)
    {
        // Validation:
        // - Epic can have Story children
        // - Story can have Task children
        // - Task cannot have children
        // - Prevent circular dependencies
        // - Enforce same tenant
    }
}

Database Migration:

ALTER TABLE issues
ADD COLUMN parent_issue_id UUID NULL,
ADD CONSTRAINT fk_issues_parent
  FOREIGN KEY (parent_issue_id) REFERENCES issues(id);

CREATE INDEX ix_issues_parent_issue_id
ON issues(parent_issue_id);

New API Endpoints:

  • POST /api/issues/{issueId}/add-child - Add child issue
  • DELETE /api/issues/{issueId}/remove-child/{childId} - Remove child
  • GET /api/issues/{issueId}/children - Get all children
  • GET /api/issues/{issueId}/hierarchy - Get full tree (recursive)

Status: TO BE IMPLEMENTED (Day 15-17)

Phase 3: Deprecate ProjectManagement Module (Future)

Timeline: Post-M1 (M2 or later)

Actions:

  1. Mark ProjectManagement module as deprecated
  2. Add migration path documentation (if needed)
  3. Remove unused code in cleanup phase

Reason:

  • No need to maintain two implementations
  • Issue Management is more mature and tested
  • Reduces codebase complexity

Status: 📋 PLANNED FOR M2


Architecture Principles

1. Single Source of Truth

  • Issue Management Module is the ONLY source for Epic/Story/Task data
  • ProjectManagement module will NOT be used in M1

2. Hierarchy Rules (DDD Business Logic)

Epic (IssueType.Epic)
  ├─ Story (IssueType.Story)
  │   ├─ Task (IssueType.Task)
  │   └─ Task (IssueType.Task)
  └─ Story (IssueType.Story)

Bug (IssueType.Bug)
  - Can be standalone OR child of Story
  - Cannot have children

Validation Rules:

  1. Epic → can have Story children only
  2. Story → can have Task/Bug children only
  3. Task → cannot have children (leaf node)
  4. Bug → can be child of Story, cannot have children
  5. Max depth: 3 levels (Epic → Story → Task)
  6. Circular dependency prevention (recursive check)
  7. Same tenant enforcement (parent and child must share TenantId)

3. Performance Optimization

  • Use PostgreSQL recursive CTEs for hierarchy queries
  • Cache frequently accessed hierarchy trees (Redis)
  • Limit depth to 3 levels (prevent infinite recursion)
  • Index on parent_issue_id for fast lookups

4. Multi-Tenant Security

  • All hierarchy queries filtered by TenantId
  • Parent-child links cannot cross tenant boundaries
  • EF Core Global Query Filters automatically applied

Implementation Plan for Day 15-17

Day 15: Database & Domain Layer (6-8 hours)

Morning (3-4h): Database Design

  • Create migration: Add parent_issue_id column to issues table
  • Add foreign key constraint: fk_issues_parent
  • Add index: ix_issues_parent_issue_id
  • Run migration on dev environment
  • Verify backward compatibility (existing data unaffected)

Afternoon (3-4h): Domain Logic

  • Update Issue entity: Add ParentIssueId, ParentIssue, ChildIssues
  • Implement SetParent(Issue parent) method with validation
  • Implement RemoveParent() method
  • Add hierarchy validation rules (see above)
  • Add domain events:
    • IssueHierarchyChangedEvent
    • ChildIssueAddedEvent
    • ChildIssueRemovedEvent
  • Unit tests for domain logic (10+ test cases)

Day 16: Application & API Layer (6-8 hours)

Morning (3-4h): Commands & Queries

  • Create AddChildIssueCommand (CQRS command)
  • Create RemoveChildIssueCommand
  • Create GetIssueHierarchyQuery (recursive query using CTE)
  • Create GetChildIssuesQuery
  • Implement command handlers with validation
  • Add authorization checks (same tenant, permissions)

Afternoon (3-4h): API Endpoints

  • Add endpoints to IssuesController:
    • POST /api/issues/{id}/add-child
    • DELETE /api/issues/{id}/remove-child/{childId}
    • GET /api/issues/{id}/children
    • GET /api/issues/{id}/hierarchy
  • Swagger documentation for new endpoints
  • SignalR notifications for hierarchy changes

Day 17: Testing & Frontend Integration (4-6 hours)

Morning (2-3h): Integration Tests

  • Test: Add child issue (Epic → Story)
  • Test: Add grandchild (Story → Task)
  • Test: Prevent invalid hierarchy (Task → Story)
  • Test: Prevent circular dependency
  • Test: Multi-tenant isolation (cannot link across tenants)
  • Test: Cascade delete behavior
  • Test: Query performance (< 50ms for 100+ issues)

Afternoon (2-3h): Frontend Integration

  • Update Kanban board to show child issue count
  • Add "Create Child Issue" button on Issue detail
  • Display parent issue breadcrumb
  • Update issue list to show hierarchy indicators
  • Test real-time updates (SignalR)

Answers to Original Questions

Question 1: Architecture Relationship

Answer: Option A - Issue Management is the NEW architecture.

ProjectManagement module was an earlier incomplete attempt. Issue Management is the production implementation. We will enhance Issue Management with hierarchy support and deprecate ProjectManagement.

Question 2: M1 Task Scope

Answer: Option A - Enhance Issue Management Module with hierarchy.

"Epic/Story Hierarchy" task in M1_REMAINING_TASKS.md means:

  • Add parent-child relationship to Issue entity
  • Implement hierarchy validation rules
  • Add API endpoints for hierarchy management
  • Update frontend to support hierarchy display

NOT Option B (create new module) or Option C (merge modules).

Question 3: Multi-Tenant Isolation

Answer: Issue Management Module has 100% multi-tenant isolation.

Verified on Day 14:

  • CRITICAL security fix implemented
  • TenantContext service working correctly
  • All 8/8 integration tests passing
  • EF Core Global Query Filters verified

For hierarchy feature:

  • Automatically inherits multi-tenant isolation
  • Parent-child validation includes tenant check
  • No additional work needed (already secured)

Question 4: Frontend Integration

Answer: Frontend currently uses Issue Management API.

Current State:

  • Kanban board uses: GET /api/issues, PUT /api/issues/{id}/status
  • Issue creation uses: POST /api/issues
  • Issue detail uses: GET /api/issues/{id}

After Day 15-17:

  • Frontend will add hierarchy support using new endpoints
  • No breaking changes to existing API
  • Backward compatible (ParentIssueId is nullable)

Impact Assessment

On M1 Timeline

Before This Decision:

  • Ambiguity about which module to use
  • Risk of duplicate work
  • Potential need to migrate data
  • Estimated: 5-7 days of confusion + rework

After This Decision:

  • Clear direction: Enhance Issue Management
  • No migration needed
  • Estimated: 2-3 days focused work
  • Time Saved: 3-4 days

M1 Completion Timeline:

  • Before: Uncertain (risk of slipping to 4+ weeks)
  • After: 2-3 weeks confirmed (on track for Nov 20)

On Code Quality

Benefits:

  1. Single source of truth (no duplication)
  2. Proven architecture (CQRS + DDD)
  3. Fully tested (100% multi-tenant isolation)
  4. Production-ready foundation
  5. Clean migration path (no breaking changes)

Risks Mitigated:

  1. No data migration needed
  2. No breaking changes to frontend
  3. No need to rewrite tests
  4. No performance regressions

Technical Specifications

Database Schema Change

-- Migration: 20251104_AddIssueHierarchy

ALTER TABLE issues
ADD COLUMN parent_issue_id UUID NULL;

ALTER TABLE issues
ADD CONSTRAINT fk_issues_parent
  FOREIGN KEY (parent_issue_id)
  REFERENCES issues(id)
  ON DELETE SET NULL;  -- When parent deleted, set child's parent to NULL

CREATE INDEX ix_issues_parent_issue_id
ON issues(parent_issue_id)
WHERE parent_issue_id IS NOT NULL;  -- Partial index (PostgreSQL optimization)

-- Add check constraint for hierarchy rules
ALTER TABLE issues
ADD CONSTRAINT ck_issues_hierarchy_rules
  CHECK (
    -- Epic can have Story children only
    (type = 'Epic' AND parent_issue_id IS NULL) OR
    -- Story can have Task/Bug children or be child of Epic
    (type = 'Story') OR
    -- Task/Bug must be leaf nodes (no children)
    (type IN ('Task', 'Bug'))
  );

Domain Model Changes

// Issue.cs (Updated)

public class Issue : TenantEntity, IAggregateRoot
{
    // Existing properties...
    public IssueType Type { get; private set; }
    public string Title { get; private set; }
    public IssueStatus Status { get; private set; }

    // NEW: Hierarchy support
    public Guid? ParentIssueId { get; private set; }
    public virtual Issue? ParentIssue { get; private set; }
    public virtual ICollection<Issue> ChildIssues { get; private set; } = new List<Issue>();

    // NEW: Hierarchy methods
    public Result SetParent(Issue parent)
    {
        if (parent.TenantId != this.TenantId)
            return Result.Failure("Cannot link issues across tenants");

        if (!IsValidHierarchy(parent))
            return Result.Failure($"{parent.Type} cannot be parent of {this.Type}");

        if (WouldCreateCircularDependency(parent))
            return Result.Failure("Circular dependency detected");

        ParentIssueId = parent.Id;
        ParentIssue = parent;

        AddDomainEvent(new IssueHierarchyChangedEvent(this.Id, parent.Id));
        return Result.Success();
    }

    public void RemoveParent()
    {
        if (ParentIssueId.HasValue)
        {
            var oldParentId = ParentIssueId.Value;
            ParentIssueId = null;
            ParentIssue = null;
            AddDomainEvent(new IssueHierarchyChangedEvent(this.Id, null, oldParentId));
        }
    }

    private bool IsValidHierarchy(Issue parent)
    {
        return (parent.Type, this.Type) switch
        {
            (IssueType.Epic, IssueType.Story) => true,
            (IssueType.Story, IssueType.Task) => true,
            (IssueType.Story, IssueType.Bug) => true,
            _ => false
        };
    }

    private bool WouldCreateCircularDependency(Issue proposedParent)
    {
        var current = proposedParent;
        int depth = 0;

        while (current != null && depth < 10)  // Safety limit
        {
            if (current.Id == this.Id)
                return true;  // Circular dependency detected

            current = current.ParentIssue;
            depth++;
        }

        return false;
    }

    public int GetDepth()
    {
        int depth = 0;
        var current = this.ParentIssue;

        while (current != null && depth < 10)
        {
            depth++;
            current = current.ParentIssue;
        }

        return depth;
    }
}

API Contract

// POST /api/issues/{id}/add-child
public class AddChildIssueRequest
{
    public Guid ChildIssueId { get; set; }
}

public class AddChildIssueResponse
{
    public bool Success { get; set; }
    public string Message { get; set; }
    public IssueDto Issue { get; set; }
}

// GET /api/issues/{id}/hierarchy
public class IssueHierarchyDto
{
    public Guid Id { get; set; }
    public string Title { get; set; }
    public IssueType Type { get; set; }
    public IssueStatus Status { get; set; }
    public List<IssueHierarchyDto> Children { get; set; }
    public int Depth { get; set; }
}

Query Performance (CTE)

-- Get complete hierarchy tree
WITH RECURSIVE hierarchy AS (
    -- Base case: Root issue
    SELECT
        id,
        tenant_id,
        parent_issue_id,
        title,
        type,
        status,
        0 AS depth
    FROM issues
    WHERE id = @rootIssueId
      AND tenant_id = @tenantId

    UNION ALL

    -- Recursive case: Children
    SELECT
        i.id,
        i.tenant_id,
        i.parent_issue_id,
        i.title,
        i.type,
        i.status,
        h.depth + 1
    FROM issues i
    INNER JOIN hierarchy h ON i.parent_issue_id = h.id
    WHERE i.tenant_id = @tenantId
      AND h.depth < 3  -- Max depth limit
)
SELECT * FROM hierarchy
ORDER BY depth, title;

Performance Target: < 50ms for 100+ issues in tree


Risks and Mitigations

Risk 1: Performance Degradation

Impact: Medium Probability: Low Mitigation:

  • Use CTE for recursive queries (PostgreSQL optimized)
  • Add index on parent_issue_id
  • Limit depth to 3 levels
  • Cache frequently accessed trees (Redis)
  • Performance test: 100+ issues scenario

Risk 2: Data Integrity Issues

Impact: High Probability: Low Mitigation:

  • Database foreign key constraints
  • Domain validation rules (DDD)
  • Transaction isolation
  • Comprehensive integration tests (10+ scenarios)
  • Circular dependency detection

Risk 3: Frontend Breaking Changes

Impact: Low Probability: Very Low Mitigation:

  • Backward compatible API (ParentIssueId nullable)
  • Existing endpoints unchanged
  • New endpoints additive only
  • Frontend can adopt gradually

Risk 4: Multi-Tenant Security Breach

Impact: Critical Probability: Very Low (Already mitigated on Day 14) Mitigation:

  • Tenant validation in SetParent method
  • EF Core Global Query Filters
  • Integration tests for cross-tenant scenarios
  • Code review by security-focused reviewer

Success Criteria

Functional Requirements

  • Can create Epic → Story → Task hierarchy
  • Can add/remove parent-child relationships via API
  • Can query full hierarchy tree
  • Hierarchy rules enforced (validation)
  • Circular dependency prevention works

Non-Functional Requirements

  • Query performance < 50ms (100+ issues)
  • Multi-tenant isolation 100% verified
  • Backward compatible (no breaking changes)
  • Integration tests pass rate ≥ 95%
  • API response time < 100ms

Documentation Requirements

  • API documentation updated (Swagger)
  • Database schema documented
  • Frontend integration guide
  • Migration guide (if needed)

Approval and Sign-off

Proposed By: Product Manager Agent Date: 2025-11-04

Approved By:

  • Architect Agent - Architecture review
  • Backend Agent - Implementation feasibility
  • QA Agent - Testing strategy
  • Main Coordinator - Project alignment

Status: AWAITING APPROVAL


Next Steps

  1. Immediate (Today, Day 14):

    • Share this ADR with all agents for review
    • Get approval from Architect and Backend agents
    • Update M1_REMAINING_TASKS.md with clarified scope
  2. Day 15 (Tomorrow):

    • Backend agent starts database migration
    • Begin domain layer implementation
  3. Day 16-17:

    • Complete API implementation
    • Integration testing
    • Frontend integration
  4. Post-Implementation:

    • Mark ProjectManagement module as deprecated
    • Document migration path (if external users exist)
    • Plan cleanup for M2

References


Document Version: 1.0 Last Updated: 2025-11-04 Next Review: After Day 17 implementation