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
- Overview
- Create Domain Structure
- Move Models to Domain
- Create Services
- Refactor ViewSets
- Testing & Validation
- Implementation Checklist
OVERVIEW
Objectives
- ✅ Create
domain/folder structure - ✅ Move models from
modules/todomain/ - ✅ 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:
- Create
domain/content/models.py - Copy models from
modules/writer/models.py - Update imports in
modules/writer/views.py - Create migration to ensure no data loss
- 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:
- Create
domain/planning/models.py - Copy models from
modules/planner/models.py - Update imports in
modules/planner/views.py - Create migration to ensure no data loss
- 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:
- Create
domain/billing/models.py - Copy models from
modules/billing/models.py - Move
CreditServicetodomain/billing/services/credit_service.py - Update imports in
modules/billing/views.py - 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:
-
Service Tests:
- ✅ Services can be tested independently
- ✅ Services handle errors correctly
- ✅ Services check credits before operations
- ✅ Services deduct credits after operations
-
API Compatibility Tests:
- ✅ All existing API endpoints work identically
- ✅ Response formats unchanged
- ✅ No breaking changes for frontend
- ✅ All ViewSet actions work correctly
-
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.pybackend/tests/test_planning_service.pybackend/tests/test_ideas_service.pybackend/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
KeywordViewSetto use services - Refactor
ClusterViewSetto use services - Refactor
ContentIdeasViewSetto use services - Refactor
TasksViewSetto use services - Refactor
ImagesViewSetto 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