437 lines
14 KiB
Markdown
437 lines
14 KiB
Markdown
# 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](#overview)
|
|
2. [Create Domain Structure](#create-domain-structure)
|
|
3. [Move Models to Domain](#move-models-to-domain)
|
|
4. [Create Services](#create-services)
|
|
5. [Refactor ViewSets](#refactor-viewsets)
|
|
6. [Testing & Validation](#testing--validation)
|
|
7. [Implementation Checklist](#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**:
|
|
```python
|
|
# 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**:
|
|
```python
|
|
# 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**:
|
|
```python
|
|
# 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)**:
|
|
```python
|
|
# 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)**:
|
|
```python
|
|
# 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)**:
|
|
```python
|
|
# 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)**:
|
|
```python
|
|
# 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**
|
|
|