9 phases
This commit is contained in:
436
docs/planning/phases/PHASE-1-SERVICE-LAYER-REFACTORING.md
Normal file
436
docs/planning/phases/PHASE-1-SERVICE-LAYER-REFACTORING.md
Normal file
@@ -0,0 +1,436 @@
|
||||
# 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**
|
||||
|
||||
Reference in New Issue
Block a user