Files
igny8/docs/planning/phases/PHASE-1-SERVICE-LAYER-REFACTORING.md
2025-11-16 23:15:45 +05:00

14 KiB

PHASE 1: SERVICE LAYER REFACTORING

Detailed Implementation Plan

Goal: Extract business logic from ViewSets into services, preserving all existing functionality.

Timeline: 2-3 weeks
Priority: HIGH
Dependencies: Phase 0


TABLE OF CONTENTS

  1. Overview
  2. Create Domain Structure
  3. Move Models to Domain
  4. Create Services
  5. Refactor ViewSets
  6. Testing & Validation
  7. Implementation Checklist

OVERVIEW

Objectives

  • Create domain/ folder structure
  • Move models from modules/ to domain/
  • Extract business logic from ViewSets to services
  • Keep ViewSets as thin wrappers
  • Preserve all existing API functionality

Key Principles

  • Backward Compatibility: All APIs remain unchanged
  • Service Layer Pattern: Business logic in services, not ViewSets
  • No Breaking Changes: Response formats unchanged
  • Testable Services: Services can be tested independently

CREATE DOMAIN STRUCTURE

1.1 Create Domain Structure

Purpose: Organize code by business domains, not technical layers.

Folder Structure

backend/igny8_core/
├── domain/                        # NEW: Domain layer
│   ├── content/                   # Content domain
│   │   ├── __init__.py
│   │   ├── models.py              # Content, Tasks, Images
│   │   ├── services/
│   │   │   ├── __init__.py
│   │   │   ├── content_generation_service.py
│   │   │   ├── content_pipeline_service.py
│   │   │   └── content_versioning_service.py
│   │   └── migrations/
│   │
│   ├── planning/                  # Planning domain
│   │   ├── __init__.py
│   │   ├── models.py              # Keywords, Clusters, Ideas
│   │   ├── services/
│   │   │   ├── __init__.py
│   │   │   ├── clustering_service.py
│   │   │   └── ideas_service.py
│   │   └── migrations/
│   │
│   ├── billing/                   # Billing domain (already exists)
│   │   ├── models.py              # Credits, Transactions
│   │   └── services/
│   │       └── credit_service.py  # Already exists
│   │
│   └── automation/                # Automation domain (Phase 2)
│       ├── models.py
│       └── services/

Implementation Tasks

Task File Current Location New Location Risk
Create domain/ folder backend/igny8_core/domain/ N/A NEW LOW
Create content domain domain/content/ N/A NEW LOW
Create planning domain domain/planning/ N/A NEW LOW
Create billing domain domain/billing/ modules/billing/ MOVE LOW
Create automation domain domain/automation/ N/A NEW (Phase 2) LOW

MOVE MODELS TO DOMAIN

1.2 Move Models to Domain

Purpose: Move models from modules/ to domain/ to separate business logic from API layer.

Content Models Migration

Model Current Location New Location Changes Needed
Content modules/writer/models.py domain/content/models.py Move, update imports
Tasks modules/writer/models.py domain/content/models.py Move, update imports
Images modules/writer/models.py domain/content/models.py Move, update imports

Migration Steps:

  1. Create domain/content/models.py
  2. Copy models from modules/writer/models.py
  3. Update imports in modules/writer/views.py
  4. Create migration to ensure no data loss
  5. Update all references to models

Planning Models Migration

Model Current Location New Location Changes Needed
Keywords modules/planner/models.py domain/planning/models.py Move, update imports
Clusters modules/planner/models.py domain/planning/models.py Move, update imports
ContentIdeas modules/planner/models.py domain/planning/models.py Move, update imports

Migration Steps:

  1. Create domain/planning/models.py
  2. Copy models from modules/planner/models.py
  3. Update imports in modules/planner/views.py
  4. Create migration to ensure no data loss
  5. Update all references to models

Billing Models Migration

Model Current Location New Location Changes Needed
CreditTransaction modules/billing/models.py domain/billing/models.py Move, update imports
CreditUsageLog modules/billing/models.py domain/billing/models.py Move, update imports

Migration Steps:

  1. Create domain/billing/models.py
  2. Copy models from modules/billing/models.py
  3. Move CreditService to domain/billing/services/credit_service.py
  4. Update imports in modules/billing/views.py
  5. Create migration to ensure no data loss

CREATE SERVICES

1.3 Create Services

Purpose: Extract business logic from ViewSets into reusable services.

ContentService

Task File Purpose Dependencies
Create ContentService domain/content/services/content_generation_service.py Unified content generation Existing Writer logic, CreditService

ContentService Methods:

# domain/content/services/content_generation_service.py
class ContentGenerationService:
    def __init__(self):
        self.credit_service = CreditService()
    
    def generate_content(self, task, account):
        """Generate content for a task"""
        # Check credits
        self.credit_service.check_credits(account, 'content_generation', task.estimated_word_count)
        
        # Generate content (existing logic from Writer ViewSet)
        content = self._generate(task)
        
        # Deduct credits
        self.credit_service.deduct_credits(account, 'content_generation', content.word_count)
        
        return content
    
    def _generate(self, task):
        """Internal content generation logic"""
        # Move logic from Writer ViewSet here
        pass

PlanningService

Task File Purpose Dependencies
Create PlanningService domain/planning/services/clustering_service.py Keyword clustering Existing Planner logic, CreditService

PlanningService Methods:

# domain/planning/services/clustering_service.py
class ClusteringService:
    def __init__(self):
        self.credit_service = CreditService()
    
    def cluster_keywords(self, keyword_ids, account):
        """Cluster keywords using AI"""
        # Check credits
        self.credit_service.check_credits(account, 'clustering', len(keyword_ids))
        
        # Cluster keywords (existing logic from Planner ViewSet)
        clusters = self._cluster(keyword_ids)
        
        # Deduct credits
        self.credit_service.deduct_credits(account, 'clustering', len(keyword_ids))
        
        return clusters

IdeasService

Task File Purpose Dependencies
Create IdeasService domain/planning/services/ideas_service.py Generate content ideas Existing Planner logic, CreditService

IdeasService Methods:

# domain/planning/services/ideas_service.py
class IdeasService:
    def __init__(self):
        self.credit_service = CreditService()
    
    def generate_ideas(self, cluster_ids, account):
        """Generate content ideas from clusters"""
        # Check credits
        self.credit_service.check_credits(account, 'idea_generation', len(cluster_ids))
        
        # Generate ideas (existing logic from Planner ViewSet)
        ideas = self._generate_ideas(cluster_ids)
        
        # Deduct credits
        self.credit_service.deduct_credits(account, 'idea_generation', len(ideas))
        
        return ideas

REFACTOR VIEWSETS

1.4 Refactor ViewSets (Keep APIs Working)

Purpose: Make ViewSets thin wrappers that delegate to services.

Planner ViewSets Refactoring

ViewSet Current New Risk
KeywordViewSet Business logic in views Delegate to services LOW
ClusterViewSet Business logic in views Delegate to services LOW
ContentIdeasViewSet Business logic in views Delegate to services LOW

Before (Business Logic in ViewSet):

# modules/planner/views.py
class ClusterViewSet(SiteSectorModelViewSet):
    @action(detail=False, methods=['post'])
    def auto_generate_ideas(self, request):
        cluster_ids = request.data.get('cluster_ids')
        # Business logic here (50+ lines)
        clusters = Cluster.objects.filter(id__in=cluster_ids)
        # AI call logic
        # Idea creation logic
        # etc.
        return Response(...)

After (Delegate to Service):

# modules/planner/views.py
class ClusterViewSet(SiteSectorModelViewSet):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.ideas_service = IdeasService()
    
    @action(detail=False, methods=['post'])
    def auto_generate_ideas(self, request):
        cluster_ids = request.data.get('cluster_ids')
        account = request.account
        
        # Delegate to service
        ideas = self.ideas_service.generate_ideas(cluster_ids, account)
        
        # Serialize and return
        serializer = ContentIdeasSerializer(ideas, many=True)
        return Response(serializer.data)

Writer ViewSets Refactoring

ViewSet Current New Risk
TasksViewSet Business logic in views Delegate to services LOW
ImagesViewSet Business logic in views Delegate to services LOW

Before (Business Logic in ViewSet):

# modules/writer/views.py
class TasksViewSet(SiteSectorModelViewSet):
    @action(detail=False, methods=['post'])
    def auto_generate_content(self, request):
        task_ids = request.data.get('task_ids')
        # Business logic here (100+ lines)
        tasks = Task.objects.filter(id__in=task_ids)
        # AI call logic
        # Content creation logic
        # etc.
        return Response(...)

After (Delegate to Service):

# modules/writer/views.py
class TasksViewSet(SiteSectorModelViewSet):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.content_service = ContentGenerationService()
    
    @action(detail=False, methods=['post'])
    def auto_generate_content(self, request):
        task_ids = request.data.get('task_ids')
        account = request.account
        
        # Delegate to service
        contents = []
        for task_id in task_ids:
            task = Task.objects.get(id=task_id)
            content = self.content_service.generate_content(task, account)
            contents.append(content)
        
        # Serialize and return
        serializer = ContentSerializer(contents, many=True)
        return Response(serializer.data)

Billing ViewSets

ViewSet Current New Risk
CreditTransactionViewSet Already uses CreditService Keep as-is NONE
CreditUsageLogViewSet Already uses CreditService Keep as-is NONE

Note: Billing ViewSets already use CreditService, so no changes needed.


TESTING & VALIDATION

1.5 Testing

Test Cases:

  1. Service Tests:

    • Services can be tested independently
    • Services handle errors correctly
    • Services check credits before operations
    • Services deduct credits after operations
  2. API Compatibility Tests:

    • All existing API endpoints work identically
    • Response formats unchanged
    • No breaking changes for frontend
    • All ViewSet actions work correctly
  3. Model Migration Tests:

    • Models work after migration
    • All relationships preserved
    • No data loss during migration
    • All queries work correctly

Test Files to Create:

  • backend/tests/test_content_service.py
  • backend/tests/test_planning_service.py
  • backend/tests/test_ideas_service.py
  • backend/tests/test_viewset_refactoring.py

IMPLEMENTATION CHECKLIST

Backend Tasks

  • Create domain/ folder structure
  • Create domain/content/ folder
  • Create domain/planning/ folder
  • Create domain/billing/ folder (move existing)
  • Move Content models to domain/content/models.py
  • Move Planning models to domain/planning/models.py
  • Move Billing models to domain/billing/models.py
  • Create migrations for model moves
  • Create ContentGenerationService
  • Create ClusteringService
  • Create IdeasService
  • Refactor KeywordViewSet to use services
  • Refactor ClusterViewSet to use services
  • Refactor ContentIdeasViewSet to use services
  • Refactor TasksViewSet to use services
  • Refactor ImagesViewSet to use services
  • Update all imports
  • Test all API endpoints

Testing Tasks

  • Test all existing API endpoints work
  • Test response formats unchanged
  • Test services independently
  • Test model migrations
  • Test backward compatibility

RISK ASSESSMENT

Risk Level Mitigation
Breaking API changes MEDIUM Extensive testing, keep response formats identical
Import errors MEDIUM Update all imports systematically
Data loss during migration LOW Backup before migration, test on staging
Service logic errors MEDIUM Unit tests for all services

SUCCESS CRITERIA

  • All existing API endpoints work identically
  • Response formats unchanged
  • No breaking changes for frontend
  • Services are testable independently
  • Business logic extracted from ViewSets
  • ViewSets are thin wrappers
  • All models moved to domain layer

END OF PHASE 1 DOCUMENT