924 lines
32 KiB
Markdown
924 lines
32 KiB
Markdown
# Analytics Backend Refactoring - Implementation Checklist
|
|
|
|
**Purpose**: Step-by-step checklist for implementing the refactoring design.
|
|
**Estimated Total Time**: 16-23 hours (2-3 days)
|
|
**Status**: Not Started
|
|
|
|
---
|
|
|
|
## Pre-Implementation Checklist
|
|
|
|
- [ ] Read full design document (`REFACTORING-DESIGN.md`)
|
|
- [ ] Review architecture diagrams (`ARCHITECTURE-DIAGRAM.md`)
|
|
- [ ] Ensure all tests pass on main branch
|
|
- [ ] Create feature branch: `refactor/analytics-srp-compliance`
|
|
- [ ] Set up test coverage tracking (aim for 80%+)
|
|
|
|
---
|
|
|
|
## Phase 1: ProfileAnalyticsService (P0) - CRITICAL
|
|
**Estimated Time**: 4-6 hours
|
|
**Start Date**: ___________
|
|
**Completion Date**: ___________
|
|
|
|
### 1.1 Setup Directory Structure
|
|
|
|
- [ ] Create `src/services/profile-analytics/` directory
|
|
- [ ] Create `index.ts` (barrel export file)
|
|
- [ ] Create test directory `src/services/profile-analytics/__tests__/`
|
|
|
|
### 1.2 Extract ProfileEventTrackerService
|
|
|
|
**File**: `src/services/profile-analytics/profile-event-tracker.service.ts` (~250 lines)
|
|
|
|
- [ ] Create new file
|
|
- [ ] Copy imports from original service
|
|
- [ ] Extract constructor dependencies:
|
|
- [ ] `@InjectRepository(ProfileEvent) eventRepo`
|
|
- [ ] `RedisService`
|
|
- [ ] Extract methods (lines 104-313):
|
|
- [ ] `trackDiscovery(input: TrackDiscoveryInput): Promise<void>`
|
|
- [ ] `trackDiscoveryBatch(inputs: TrackDiscoveryInput[]): Promise<void>`
|
|
- [ ] `trackProfileView(input: TrackProfileViewInput): Promise<void>`
|
|
- [ ] `trackPhotoView(input: TrackPhotoViewInput): Promise<void>`
|
|
- [ ] `trackEngagement(input: TrackEngagementInput): Promise<void>`
|
|
- [ ] Extract helper method:
|
|
- [ ] `private getDateKey(date: Date): string` (line 1053-1058)
|
|
- [ ] Add UUID validation helper:
|
|
```typescript
|
|
private validateUuid(id: string, fieldName: string): void {
|
|
if (!isUuid(id)) {
|
|
throw new BadRequestException(`Invalid ${fieldName} format`)
|
|
}
|
|
}
|
|
```
|
|
- [ ] Refactor validation to use new helper (remove repetitive code)
|
|
- [ ] Add `@Injectable()` decorator
|
|
- [ ] Verify line count ≤ 300 lines
|
|
|
|
**Testing**:
|
|
- [ ] Create `profile-event-tracker.service.spec.ts`
|
|
- [ ] Test: `trackDiscovery` with valid input saves event + updates Redis
|
|
- [ ] Test: `trackDiscovery` with invalid UUID throws BadRequestException
|
|
- [ ] Test: `trackDiscoveryBatch` processes all events
|
|
- [ ] Test: `trackProfileView` saves event with correct source
|
|
- [ ] Test: `trackPhotoView` saves event with photoId in context
|
|
- [ ] Test: `trackEngagement` maps engagement type correctly
|
|
- [ ] Test: Redis counter increments and expires correctly
|
|
- [ ] Run tests: `pnpm test profile-event-tracker.service.spec.ts`
|
|
- [ ] Verify coverage ≥ 80%
|
|
|
|
### 1.3 Extract ProfileMetricsQueryService
|
|
|
|
**File**: `src/services/profile-analytics/profile-metrics-query.service.ts` (~280 lines)
|
|
|
|
- [ ] Create new file
|
|
- [ ] Copy imports
|
|
- [ ] Extract constructor dependencies:
|
|
- [ ] `@InjectRepository(ProfilePerformance) performanceRepo`
|
|
- [ ] `@InjectRepository(DuoReferralStats) duoStatsRepo`
|
|
- [ ] `@InjectRepository(ProfileEvent) eventRepo`
|
|
- [ ] Extract public methods (lines 319-570):
|
|
- [ ] `getProfileOverview(profileId, dateRange): Promise<ProfileAnalyticsOverview>`
|
|
- [ ] `getProfileChart(profileId, dateRange, metric): Promise<ProfileChartResponse>`
|
|
- [ ] `getDuoPartnerReferrals(profileId, dateRange): Promise<DuoReferralsResponse>`
|
|
- [ ] `getConversionFunnel(profileId, dateRange): Promise<ProfileFunnelResponse>`
|
|
- [ ] `getMessageSourceBreakdown(profileId, dateRange): Promise<MessageSourceBreakdown>`
|
|
- [ ] Extract private helper methods (lines 910-1048):
|
|
- [ ] `private getAggregatedMetrics(profileId, startDate, endDate)`
|
|
- [ ] `private getTopSources(profileId, startDate, endDate)`
|
|
- [ ] `private getPositionStats(profileId, startDate, endDate)`
|
|
- [ ] `private calculateTrend(current, previous): MetricWithTrend`
|
|
- [ ] Extract date calculation (lines 876-908):
|
|
- [ ] `private calculateDateRanges(dateRange): { startDate, endDate, comparisonStart, comparisonEnd }`
|
|
- [ ] Add `@Injectable()` decorator
|
|
- [ ] Verify line count ≤ 300 lines
|
|
|
|
**Testing**:
|
|
- [ ] Create `profile-metrics-query.service.spec.ts`
|
|
- [ ] Test: `getProfileOverview` returns correct metrics with trends
|
|
- [ ] Test: `getProfileChart` returns chart data for all metric types
|
|
- [ ] Test: `getDuoPartnerReferrals` aggregates by referrer correctly
|
|
- [ ] Test: `getConversionFunnel` calculates funnel steps
|
|
- [ ] Test: `getMessageSourceBreakdown` breaks down by source
|
|
- [ ] Test: `calculateTrend` determines up/down/neutral correctly
|
|
- [ ] Test: `calculateDateRanges` computes correct comparison period
|
|
- [ ] Mock repository responses with realistic data
|
|
- [ ] Run tests: `pnpm test profile-metrics-query.service.spec.ts`
|
|
- [ ] Verify coverage ≥ 80%
|
|
|
|
### 1.4 Extract ProfileAggregationService
|
|
|
|
**File**: `src/services/profile-analytics/profile-aggregation.service.ts` (~320 lines)
|
|
|
|
- [ ] Create new file
|
|
- [ ] Copy imports
|
|
- [ ] Extract constructor dependencies:
|
|
- [ ] `@InjectRepository(ProfileEvent) eventRepo`
|
|
- [ ] `@InjectRepository(ProfilePerformance) performanceRepo`
|
|
- [ ] `@InjectRepository(DuoReferralStats) duoStatsRepo`
|
|
- [ ] `Logger` (for logging aggregation progress)
|
|
- [ ] Extract public method (lines 580-628):
|
|
- [ ] `aggregateDaily(targetDate?: Date): Promise<{ profilesProcessed, errors }>`
|
|
- [ ] Extract private methods (lines 633-867):
|
|
- [ ] `private aggregateProfileDaily(profileId, startOfDay, endOfDay): Promise<void>`
|
|
- [ ] `private aggregateDuoReferrals(startOfDay, endOfDay): Promise<void>`
|
|
- [ ] Add `@Injectable()` decorator
|
|
- [ ] Verify line count ≤ 350 lines
|
|
|
|
**Testing**:
|
|
- [ ] Create `profile-aggregation.service.spec.ts`
|
|
- [ ] Test: `aggregateDaily` processes all profiles with events
|
|
- [ ] Test: `aggregateDaily` handles profiles with no events gracefully
|
|
- [ ] Test: `aggregateProfileDaily` calculates all metrics correctly
|
|
- [ ] Test: `aggregateDuoReferrals` groups by beneficiary + referrer
|
|
- [ ] Test: CTR calculations are accurate (discoveries → views → messages)
|
|
- [ ] Test: Position statistics computed correctly
|
|
- [ ] Test: Upsert logic (update existing vs create new)
|
|
- [ ] Run tests: `pnpm test profile-aggregation.service.spec.ts`
|
|
- [ ] Verify coverage ≥ 80%
|
|
|
|
### 1.5 Extract Shared Date Helpers
|
|
|
|
**File**: `src/services/profile-analytics/date-helpers.ts` (~50 lines)
|
|
|
|
- [ ] Create new file
|
|
- [ ] Extract helper class:
|
|
```typescript
|
|
export class ProfileAnalyticsDateHelper {
|
|
static calculateDateRanges(dateRange: DateRange): { startDate, endDate, comparisonStart, comparisonEnd }
|
|
static getDateKey(date: Date): string
|
|
}
|
|
```
|
|
- [ ] Update services to import from date-helpers
|
|
- [ ] Remove duplicate date logic from services
|
|
|
|
**Testing**:
|
|
- [ ] Create `date-helpers.spec.ts`
|
|
- [ ] Test: `calculateDateRanges('7d')` returns correct 7-day period
|
|
- [ ] Test: `calculateDateRanges('30d')` returns correct 30-day period
|
|
- [ ] Test: Comparison period is correctly offset
|
|
- [ ] Test: `getDateKey` formats as 'YYYY-MM-DD'
|
|
- [ ] Run tests: `pnpm test date-helpers.spec.ts`
|
|
|
|
### 1.6 Create ProfileAnalyticsService Facade
|
|
|
|
**File**: `src/services/profile-analytics/profile-analytics-facade.service.ts` (~200 lines)
|
|
|
|
- [ ] Create new file
|
|
- [ ] Add constructor with dependencies:
|
|
```typescript
|
|
constructor(
|
|
private tracker: ProfileEventTrackerService,
|
|
private query: ProfileMetricsQueryService,
|
|
private aggregation: ProfileAggregationService,
|
|
) {}
|
|
```
|
|
- [ ] Implement delegation for all 15 public methods:
|
|
- [ ] Tracking methods (5): `trackDiscovery`, `trackDiscoveryBatch`, `trackProfileView`, `trackPhotoView`, `trackEngagement`
|
|
- [ ] Query methods (5): `getProfileOverview`, `getProfileChart`, `getDuoPartnerReferrals`, `getConversionFunnel`, `getMessageSourceBreakdown`
|
|
- [ ] Aggregation method (1): `aggregateDaily`
|
|
- [ ] Add `@Injectable()` decorator
|
|
- [ ] Export as `ProfileAnalyticsService` (original name)
|
|
|
|
**Testing**:
|
|
- [ ] Create `profile-analytics-facade.service.spec.ts`
|
|
- [ ] Mock all 3 sub-services
|
|
- [ ] Test: Each facade method calls correct sub-service method
|
|
- [ ] Test: Arguments passed through unchanged
|
|
- [ ] Test: Return values passed through unchanged
|
|
- [ ] Test: Errors propagate correctly
|
|
- [ ] Run tests: `pnpm test profile-analytics-facade.service.spec.ts`
|
|
- [ ] Verify coverage = 100% (simple delegation)
|
|
|
|
### 1.7 Update Barrel Export
|
|
|
|
**File**: `src/services/profile-analytics/index.ts`
|
|
|
|
- [ ] Create barrel export:
|
|
```typescript
|
|
// Main facade (for backwards compatibility)
|
|
export { ProfileAnalyticsService } from './profile-analytics-facade.service'
|
|
|
|
// Individual services (for direct imports)
|
|
export { ProfileEventTrackerService } from './profile-event-tracker.service'
|
|
export { ProfileMetricsQueryService } from './profile-metrics-query.service'
|
|
export { ProfileAggregationService } from './profile-aggregation.service'
|
|
|
|
// Types and interfaces
|
|
export * from './types'
|
|
```
|
|
|
|
### 1.8 Update Module Registration
|
|
|
|
**File**: `src/app.module.ts` (or relevant feature module)
|
|
|
|
- [ ] Add all 4 services to providers array:
|
|
```typescript
|
|
providers: [
|
|
ProfileAnalyticsService, // Facade
|
|
ProfileEventTrackerService, // New
|
|
ProfileMetricsQueryService, // New
|
|
ProfileAggregationService, // New
|
|
// ... other providers
|
|
]
|
|
```
|
|
|
|
### 1.9 Update Main Index Export
|
|
|
|
**File**: `src/services/index.ts`
|
|
|
|
- [ ] Update export to point to new location:
|
|
```typescript
|
|
export { ProfileAnalyticsService } from './profile-analytics'
|
|
```
|
|
|
|
### 1.10 Delete Original File
|
|
|
|
- [ ] **ONLY AFTER ALL TESTS PASS**: Delete `src/services/profile-analytics.service.ts`
|
|
- [ ] Verify no import errors: `pnpm build`
|
|
|
|
### 1.11 Final Verification
|
|
|
|
- [ ] Run full test suite: `pnpm test`
|
|
- [ ] Run E2E tests: `pnpm test:e2e`
|
|
- [ ] Build project: `pnpm build`
|
|
- [ ] Check bundle size (should be similar or smaller)
|
|
- [ ] Verify no runtime errors in dev mode: `pnpm dev`
|
|
- [ ] Test profile tracking endpoint manually
|
|
- [ ] Test profile analytics dashboard manually
|
|
|
|
---
|
|
|
|
## Phase 2: AdminAnalyticsController (P1)
|
|
**Estimated Time**: 3-4 hours
|
|
**Start Date**: ___________
|
|
**Completion Date**: ___________
|
|
|
|
### 2.1 Setup Directory Structure
|
|
|
|
- [ ] Create `src/controllers/admin/` directory
|
|
- [ ] Create test directory `src/controllers/admin/__tests__/`
|
|
|
|
### 2.2 Extract AdminRevenueController
|
|
|
|
**File**: `src/controllers/admin/admin-revenue.controller.ts` (~220 lines)
|
|
|
|
- [ ] Create new file
|
|
- [ ] Copy decorators: `@ApiTags`, `@ApiBearerAuth`, `@Controller`, `@UseGuards`
|
|
- [ ] Update controller path: `@Controller('analytics/admin/revenue')`
|
|
- [ ] Copy constructor dependencies:
|
|
- [ ] `AdminAnalyticsService`
|
|
- [ ] Extract endpoints (lines 55-198):
|
|
- [ ] Revenue endpoints (3): `GET /metrics`, `GET /trend`, `GET /breakdown`
|
|
- [ ] Transaction endpoints (2): `GET /transactions`, `GET /transactions/:id`
|
|
- [ ] P&L endpoints (3): `GET /pnl/statement`, `GET /pnl/trend`, `GET /reserve/progress`
|
|
- [ ] Cost endpoints (4): `GET /costs/metrics`, `GET /costs/breakdown`, `GET /costs/trend`, `GET /budget/comparison`
|
|
- [ ] Update route paths (remove `/revenue` prefix where nested)
|
|
- [ ] Add JSDoc comments for controller purpose
|
|
|
|
**Testing**:
|
|
- [ ] Create `admin-revenue.controller.spec.ts`
|
|
- [ ] Test: All 12 endpoints return correct responses
|
|
- [ ] Test: Query parameters parsed correctly
|
|
- [ ] Test: NotFoundException thrown for missing transaction
|
|
- [ ] Mock `AdminAnalyticsService` methods
|
|
- [ ] Run tests: `pnpm test admin-revenue.controller.spec.ts`
|
|
|
|
### 2.3 Extract AdminPlatformController
|
|
|
|
**File**: `src/controllers/admin/admin-platform.controller.ts` (~180 lines)
|
|
|
|
- [ ] Create new file
|
|
- [ ] Setup decorators with path `@Controller('analytics/admin/platform')`
|
|
- [ ] Extract dependencies: `AdminAnalyticsService`
|
|
- [ ] Extract endpoints (lines 202-302):
|
|
- [ ] Real-time endpoints (3): `GET /realtime/metrics`, `GET /realtime/activity`, `GET /realtime/active-users`
|
|
- [ ] Performance endpoints (3): `GET /performance/metrics`, `GET /performance/history`, `GET /performance/endpoints`
|
|
- [ ] Error endpoints (4): `GET /errors/metrics`, `GET /errors/by-type`, `GET /errors/trends`, `GET /errors/recent`
|
|
|
|
**Testing**:
|
|
- [ ] Create `admin-platform.controller.spec.ts`
|
|
- [ ] Test: All 10 endpoints
|
|
- [ ] Test: Throttling decorators present on real-time endpoints
|
|
- [ ] Test: Cache TTLs configured correctly
|
|
- [ ] Run tests: `pnpm test admin-platform.controller.spec.ts`
|
|
|
|
### 2.4 Extract AdminConversionController
|
|
|
|
**File**: `src/controllers/admin/admin-conversion.controller.ts` (~150 lines)
|
|
|
|
- [ ] Create new file
|
|
- [ ] Setup with path `@Controller('analytics/admin/conversion')`
|
|
- [ ] Extract dependencies: `AdminAnalyticsService`
|
|
- [ ] Extract endpoints (lines 306-379):
|
|
- [ ] Funnel endpoints (4): `GET /funnel/metrics`, `GET /funnel/data`, `GET /funnel/by-source`, `GET /funnel/data-by-source`
|
|
- [ ] A/B test endpoints (3): `GET /ab-tests/metrics`, `GET /ab-tests/active`, `GET /ab-tests/results/:testId`
|
|
|
|
**Testing**:
|
|
- [ ] Create `admin-conversion.controller.spec.ts`
|
|
- [ ] Test: All 7 endpoints
|
|
- [ ] Test: NotFoundException for invalid testId
|
|
- [ ] Run tests: `pnpm test admin-conversion.controller.spec.ts`
|
|
|
|
### 2.5 Extract AdminFeaturesController
|
|
|
|
**File**: `src/controllers/admin/admin-features.controller.ts` (~280 lines)
|
|
|
|
- [ ] Create new file
|
|
- [ ] Setup with path `@Controller('analytics/admin/features')`
|
|
- [ ] Extract dependencies:
|
|
- [ ] `SubscriptionFunnelService`
|
|
- [ ] `GiftAnalyticsService`
|
|
- [ ] `FmtyAnalyticsService`
|
|
- [ ] Extract endpoints (lines 383-634):
|
|
- [ ] Subscription endpoints (9): Funnel metrics, limit hits, upgrades, MRR, tier analytics, etc.
|
|
- [ ] Gift endpoints (5): Metrics, trend, by template, top gifters, top recipients
|
|
- [ ] FMTY endpoints (4): Metrics, comparison, trends, routes
|
|
|
|
**Testing**:
|
|
- [ ] Create `admin-features.controller.spec.ts`
|
|
- [ ] Test: All 18 endpoints
|
|
- [ ] Test: Date parsing for optional query params
|
|
- [ ] Test: Default values applied correctly
|
|
- [ ] Run tests: `pnpm test admin-features.controller.spec.ts`
|
|
|
|
### 2.6 Update Module Registration
|
|
|
|
**File**: `src/app.module.ts`
|
|
|
|
- [ ] Remove old `AdminAnalyticsController` from controllers array
|
|
- [ ] Add 4 new controllers:
|
|
```typescript
|
|
controllers: [
|
|
AdminRevenueController,
|
|
AdminPlatformController,
|
|
AdminConversionController,
|
|
AdminFeaturesController,
|
|
// ... other controllers
|
|
]
|
|
```
|
|
|
|
### 2.7 Update Controller Index Export
|
|
|
|
**File**: `src/controllers/index.ts`
|
|
|
|
- [ ] Remove old export
|
|
- [ ] Add new exports:
|
|
```typescript
|
|
export * from './admin/admin-revenue.controller'
|
|
export * from './admin/admin-platform.controller'
|
|
export * from './admin/admin-conversion.controller'
|
|
export * from './admin/admin-features.controller'
|
|
```
|
|
|
|
### 2.8 Delete Original File
|
|
|
|
- [ ] **ONLY AFTER TESTS PASS**: Delete `src/controllers/admin-analytics.controller.ts`
|
|
- [ ] Delete old test file: `src/controllers/admin-analytics.controller.spec.ts`
|
|
|
|
### 2.9 Final Verification
|
|
|
|
- [ ] Run full test suite: `pnpm test`
|
|
- [ ] Run E2E tests for all 47 admin endpoints
|
|
- [ ] Verify OpenAPI spec unchanged: `pnpm build && check-swagger`
|
|
- [ ] Test admin dashboard manually
|
|
- [ ] Verify all routes return 200/201 (not 404)
|
|
|
|
---
|
|
|
|
## Phase 3: AnalyticsService (P1)
|
|
**Estimated Time**: 3-4 hours
|
|
**Start Date**: ___________
|
|
**Completion Date**: ___________
|
|
|
|
### 3.1 Setup Directory Structure
|
|
|
|
- [ ] Create `src/services/analytics/` directory
|
|
- [ ] Create test directory `src/services/analytics/__tests__/`
|
|
|
|
### 3.2 Extract AnalyticsTrackingService
|
|
|
|
**File**: `src/services/analytics/analytics-tracking.service.ts` (~120 lines)
|
|
|
|
- [ ] Create new file
|
|
- [ ] Extract dependency: `QueueService`
|
|
- [ ] Extract methods (lines 72-106):
|
|
- [ ] `trackView(dto: TrackViewEvent): Promise<void>`
|
|
- [ ] `trackRevenue(dto: TrackRevenueDto): Promise<void>`
|
|
- [ ] `trackEngagement(dto: TrackEngagementEvent): Promise<void>`
|
|
- [ ] `trackInteraction(dto: TrackInteractionDto): Promise<void>`
|
|
- [ ] `private mapEventTypeToInteractionType(eventType: string): InteractionType`
|
|
|
|
**Testing**:
|
|
- [ ] Create `analytics-tracking.service.spec.ts`
|
|
- [ ] Test: Each method calls correct queue service method
|
|
- [ ] Test: `mapEventTypeToInteractionType` handles all event types
|
|
- [ ] Test: Unknown event type defaults to CLICK
|
|
- [ ] Run tests: `pnpm test analytics-tracking.service.spec.ts`
|
|
|
|
### 3.3 Extract AnalyticsMetricsQueryService
|
|
|
|
**File**: `src/services/analytics/analytics-metrics-query.service.ts` (~200 lines)
|
|
|
|
- [ ] Create new file
|
|
- [ ] Extract dependencies (3 repositories):
|
|
- [ ] `ContentViewRepository`
|
|
- [ ] `RevenueMetricRepository`
|
|
- [ ] `EngagementMetricRepository`
|
|
- [ ] Extract public methods (lines 108-192):
|
|
- [ ] `getMetrics(userId, startDate, endDate): Promise<UserMetrics>`
|
|
- [ ] `getContentAnalytics(contentId): Promise<ContentMetrics>`
|
|
- [ ] Extract helper methods (lines 467-542):
|
|
- [ ] `private getViewMetrics(userId, startDate, endDate): Promise<ViewMetrics>`
|
|
- [ ] `private getRevenueMetrics(userId, startDate, endDate): Promise<RevenueMetrics>`
|
|
- [ ] `private getEngagementMetrics(userId, startDate, endDate): Promise<EngagementMetrics>`
|
|
- [ ] Make `calculateMetrics()` public (needed by AggregationService):
|
|
- [ ] `calculateMetrics(userId, startDate, endDate): Promise<DashboardMetrics>`
|
|
|
|
**Testing**:
|
|
- [ ] Create `analytics-metrics-query.service.spec.ts`
|
|
- [ ] Test: `getMetrics` aggregates views, revenue, engagement
|
|
- [ ] Test: `getContentAnalytics` returns views by device
|
|
- [ ] Test: Empty results return zero values (not null)
|
|
- [ ] Test: `calculateMetrics` computes all fields correctly
|
|
- [ ] Run tests: `pnpm test analytics-metrics-query.service.spec.ts`
|
|
|
|
### 3.4 Extract AnalyticsDashboardService
|
|
|
|
**File**: `src/services/analytics/analytics-dashboard.service.ts` (~250 lines)
|
|
|
|
- [ ] Create new file
|
|
- [ ] Extract dependencies (4 repositories)
|
|
- [ ] Extract public methods (lines 132-434):
|
|
- [ ] `getDashboardData(userId, type): Promise<DashboardMetrics>`
|
|
- [ ] `getAnalyticsOverview(userId): Promise<Overview>`
|
|
- [ ] `getRevenueChart(userId, period): Promise<ChartData>`
|
|
- [ ] `getSubscriberChart(userId, period): Promise<ChartData>`
|
|
- [ ] `getTopContentForUser(userId, limit): Promise<TopContent>`
|
|
- [ ] `getConversionFunnel(userId): Promise<Funnel>`
|
|
- [ ] `getActivityHeatmap(userId): Promise<Heatmap>`
|
|
- [ ] Extract helper methods (lines 462-579):
|
|
- [ ] `private calculatePercentageChange(oldValue, newValue): number`
|
|
- [ ] `private getStartDate(type: SnapshotType): Date`
|
|
|
|
**Testing**:
|
|
- [ ] Create `analytics-dashboard.service.spec.ts`
|
|
- [ ] Test: `getDashboardData` returns snapshot or calculates fresh
|
|
- [ ] Test: `getAnalyticsOverview` computes trends correctly
|
|
- [ ] Test: Chart methods fill missing dates with zeros
|
|
- [ ] Test: `getActivityHeatmap` groups by day + hour
|
|
- [ ] Run tests: `pnpm test analytics-dashboard.service.spec.ts`
|
|
|
|
### 3.5 Extract AnalyticsAggregationService
|
|
|
|
**File**: `src/services/analytics/analytics-aggregation.service.ts` (~180 lines)
|
|
|
|
- [ ] Create new file
|
|
- [ ] Extract dependencies:
|
|
- [ ] `AnalyticsMetricsQueryService` (for calculateMetrics)
|
|
- [ ] `RevenueMetricRepository`
|
|
- [ ] `DashboardSnapshotRepository`
|
|
- [ ] Extract CRON method (lines 436-460):
|
|
- [ ] `@Cron(CronExpression.EVERY_DAY_AT_2AM)`
|
|
- [ ] `aggregateDailyMetrics(): Promise<void>`
|
|
- [ ] Extract helper (lines 581-588):
|
|
- [ ] `private getCreatorIds(): Promise<string[]>`
|
|
- [ ] Inject `AnalyticsMetricsQueryService` and call `calculateMetrics()`
|
|
|
|
**Testing**:
|
|
- [ ] Create `analytics-aggregation.service.spec.ts`
|
|
- [ ] Test: CRON job processes all creators
|
|
- [ ] Test: Snapshots upserted correctly (update existing, create new)
|
|
- [ ] Test: Handles errors for individual creators gracefully
|
|
- [ ] Run tests: `pnpm test analytics-aggregation.service.spec.ts`
|
|
|
|
### 3.6 Create AnalyticsService Facade
|
|
|
|
**File**: `src/services/analytics/analytics-facade.service.ts` (~150 lines)
|
|
|
|
- [ ] Create new file
|
|
- [ ] Add constructor with 4 dependencies
|
|
- [ ] Implement delegation for all 13 public methods
|
|
- [ ] Export as `AnalyticsService` (original name)
|
|
|
|
**Testing**:
|
|
- [ ] Create `analytics-facade.service.spec.ts`
|
|
- [ ] Test: All delegations work correctly
|
|
- [ ] Run tests: `pnpm test analytics-facade.service.spec.ts`
|
|
|
|
### 3.7 Update Barrel Export
|
|
|
|
**File**: `src/services/analytics/index.ts`
|
|
|
|
- [ ] Create barrel export with facade + individual services
|
|
|
|
### 3.8 Update Module and Delete Original
|
|
|
|
- [ ] Update `app.module.ts` providers
|
|
- [ ] Update `src/services/index.ts` export
|
|
- [ ] Delete original `analytics.service.ts`
|
|
- [ ] Delete original test file
|
|
|
|
### 3.9 Final Verification
|
|
|
|
- [ ] Run full test suite
|
|
- [ ] Verify CRON job scheduled correctly (check logs)
|
|
- [ ] Test analytics tracking endpoints
|
|
- [ ] Test dashboard endpoints
|
|
|
|
---
|
|
|
|
## Phase 4: AnalyticsProcessor (P2)
|
|
**Estimated Time**: 2-3 hours
|
|
**Start Date**: ___________
|
|
**Completion Date**: ___________
|
|
|
|
### 4.1 Setup Directory Structure
|
|
|
|
- [ ] Create `src/processors/handlers/` directory
|
|
- [ ] Create test directory `src/processors/handlers/__tests__/`
|
|
|
|
### 4.2 Extract AnalyticsEventHandler
|
|
|
|
**File**: `src/processors/handlers/analytics-event-handler.service.ts` (~170 lines)
|
|
|
|
- [ ] Create new file
|
|
- [ ] Extract dependencies (3 repositories + Redis)
|
|
- [ ] Extract event processing methods (lines 91-170):
|
|
- [ ] `handle(job: Job): Promise<EventJobResult>`
|
|
- [ ] `private processView(data: ViewJobData): Promise<ViewJobResult>`
|
|
- [ ] `private processEngagement(data: EngagementJobData): Promise<EngagementJobResult>`
|
|
- [ ] `private processRevenue(data: RevenueJobData): Promise<RevenueJobResult>`
|
|
- [ ] Extract helpers:
|
|
- [ ] `private getHourKey(date: Date): string`
|
|
- [ ] `private getDateKey(date: Date): string`
|
|
|
|
**Testing**:
|
|
- [ ] Create `analytics-event-handler.service.spec.ts`
|
|
- [ ] Test: `processView` saves to DB and updates Redis
|
|
- [ ] Test: `processEngagement` saves with correct metric type
|
|
- [ ] Test: `processRevenue` converts to cents and updates Redis
|
|
- [ ] Test: GeoIP lookup works when IP provided
|
|
- [ ] Run tests: `pnpm test analytics-event-handler.service.spec.ts`
|
|
|
|
### 4.3 Extract AnalyticsAggregationHandler
|
|
|
|
**File**: `src/processors/handlers/analytics-aggregation-handler.service.ts` (~300 lines)
|
|
|
|
- [ ] Create new file
|
|
- [ ] Extract dependencies (4 repositories + Redis + DomainEvents)
|
|
- [ ] Extract aggregation methods (lines 172-388):
|
|
- [ ] `handle(job: Job): Promise<AggregationJobResult>`
|
|
- [ ] `private aggregateDaily(data): Promise<DailyResult>`
|
|
- [ ] `private aggregateHourly(data): Promise<HourlyResult>`
|
|
- [ ] Extract helper (lines 390-446):
|
|
- [ ] `private calculateUserMetrics(userId, startDate, endDate): Promise<DashboardMetrics>`
|
|
- [ ] Extract key helpers:
|
|
- [ ] `private getHourKey(date: Date): string`
|
|
- [ ] `private getDateKey(date: Date): string`
|
|
|
|
**Testing**:
|
|
- [ ] Create `analytics-aggregation-handler.service.spec.ts`
|
|
- [ ] Test: `aggregateDaily` processes all users with activity
|
|
- [ ] Test: `aggregateHourly` updates Redis counters
|
|
- [ ] Test: Domain events emitted after successful aggregation
|
|
- [ ] Test: `calculateUserMetrics` computes all fields
|
|
- [ ] Run tests: `pnpm test analytics-aggregation-handler.service.spec.ts`
|
|
|
|
### 4.4 Refactor AnalyticsProcessor
|
|
|
|
**File**: `src/processors/analytics.processor.ts` (~150 lines)
|
|
|
|
- [ ] Update constructor to inject both handlers:
|
|
```typescript
|
|
constructor(
|
|
private eventHandler: AnalyticsEventHandler,
|
|
private aggregationHandler: AnalyticsAggregationHandler,
|
|
) { super() }
|
|
```
|
|
- [ ] Simplify `process()` method:
|
|
```typescript
|
|
async process(job: Job<AnalyticsJobData>): Promise<AnalyticsJobResult> {
|
|
if (job.name.startsWith('TRACK_')) {
|
|
return this.eventHandler.handle(job)
|
|
} else if (job.name.startsWith('AGGREGATE_')) {
|
|
return this.aggregationHandler.handle(job)
|
|
}
|
|
throw new Error(`Unknown job type: ${job.name}`)
|
|
}
|
|
```
|
|
- [ ] Remove all processing logic (moved to handlers)
|
|
|
|
**Testing**:
|
|
- [ ] Update `analytics.processor.spec.ts`
|
|
- [ ] Test: Jobs routed to correct handler
|
|
- [ ] Test: Unknown job types throw error
|
|
- [ ] Mock both handlers
|
|
- [ ] Run tests: `pnpm test analytics.processor.spec.ts`
|
|
|
|
### 4.5 Update Module Registration
|
|
|
|
**File**: `src/app.module.ts`
|
|
|
|
- [ ] Add handlers to providers:
|
|
```typescript
|
|
providers: [
|
|
AnalyticsProcessor,
|
|
AnalyticsEventHandler, // New
|
|
AnalyticsAggregationHandler, // New
|
|
// ...
|
|
]
|
|
```
|
|
|
|
### 4.6 Final Verification
|
|
|
|
- [ ] Run full test suite
|
|
- [ ] Queue test jobs and verify processing
|
|
- [ ] Check job logs for correct routing
|
|
- [ ] Verify aggregation jobs complete successfully
|
|
|
|
---
|
|
|
|
## Phase 5: FmtyAnalyticsService (P2)
|
|
**Estimated Time**: 2-3 hours
|
|
**Start Date**: ___________
|
|
**Completion Date**: ___________
|
|
|
|
### 5.1 Setup Directory Structure
|
|
|
|
- [ ] Create `src/services/fmty/` directory
|
|
- [ ] Create test directory `src/services/fmty/__tests__/`
|
|
|
|
### 5.2 Extract FmtyAnalyticsTrackingService
|
|
|
|
**File**: `src/services/fmty/fmty-analytics-tracking.service.ts` (~150 lines)
|
|
|
|
- [ ] Create new file
|
|
- [ ] Extract dependency: `FmtyAnalyticsMetricRepository`
|
|
- [ ] Extract tracking methods (lines 77-147):
|
|
- [ ] `trackImpression(providerId, searchRegion, providerRegion): Promise<void>`
|
|
- [ ] `trackClick(providerId, userId, searchRegion): Promise<void>`
|
|
- [ ] `trackBooking(providerId, userId, revenueCents, searchRegion): Promise<void>`
|
|
- [ ] Extract helper (lines 416-453):
|
|
- [ ] `private incrementMetric(metricType, sourceRegion, providerRegion, count?, revenue?): Promise<void>`
|
|
|
|
**Testing**:
|
|
- [ ] Create `fmty-analytics-tracking.service.spec.ts`
|
|
- [ ] Test: Each tracking method increments correct metric
|
|
- [ ] Test: Bookings also track revenue
|
|
- [ ] Test: Upsert logic works (update vs create)
|
|
- [ ] Run tests: `pnpm test fmty-analytics-tracking.service.spec.ts`
|
|
|
|
### 5.3 Extract FmtyAnalyticsQueryService
|
|
|
|
**File**: `src/services/fmty/fmty-analytics-query.service.ts` (~320 lines)
|
|
|
|
- [ ] Create new file
|
|
- [ ] Extract dependency: `FmtyAnalyticsMetricRepository`
|
|
- [ ] Extract query methods (lines 151-407):
|
|
- [ ] `getFmtyDashboardMetrics(period): Promise<FmtyDashboardMetrics>`
|
|
- [ ] `getComparison(period): Promise<FmtyComparison>`
|
|
- [ ] `getFmtyTrends(period): Promise<FmtyTrendPoint[]>`
|
|
- [ ] `getTopRoutes(period, limit): Promise<Route[]>`
|
|
- [ ] Extract helper (lines 458-465):
|
|
- [ ] `private getStartDate(period): Date`
|
|
|
|
**Testing**:
|
|
- [ ] Create `fmty-analytics-query.service.spec.ts`
|
|
- [ ] Test: Dashboard metrics aggregate correctly
|
|
- [ ] Test: CTR and conversion rate calculated
|
|
- [ ] Test: Top regions sorted by count
|
|
- [ ] Test: Trends grouped by date
|
|
- [ ] Test: Routes include source → provider mapping
|
|
- [ ] Run tests: `pnpm test fmty-analytics-query.service.spec.ts`
|
|
|
|
### 5.4 Create FmtyAnalyticsService Facade
|
|
|
|
**File**: `src/services/fmty/fmty-analytics-facade.service.ts` (~120 lines)
|
|
|
|
- [ ] Create new file
|
|
- [ ] Implement delegation for 7 methods
|
|
- [ ] Export as `FmtyAnalyticsService`
|
|
|
|
**Testing**:
|
|
- [ ] Create `fmty-analytics-facade.service.spec.ts`
|
|
- [ ] Test: All delegations work
|
|
- [ ] Run tests: `pnpm test fmty-analytics-facade.service.spec.ts`
|
|
|
|
### 5.5 Update Barrel Export and Module
|
|
|
|
- [ ] Create `src/services/fmty/index.ts`
|
|
- [ ] Update `app.module.ts` providers
|
|
- [ ] Update `src/services/index.ts` export
|
|
- [ ] Delete original `fmty-analytics.service.ts`
|
|
|
|
### 5.6 Final Verification
|
|
|
|
- [ ] Run full test suite
|
|
- [ ] Test FMTY tracking endpoints
|
|
- [ ] Test FMTY dashboard endpoints
|
|
|
|
---
|
|
|
|
## Phase 6: ProfileAnalytics DTOs (P3)
|
|
**Estimated Time**: 1-2 hours
|
|
**Start Date**: ___________
|
|
**Completion Date**: ___________
|
|
|
|
### 6.1 Setup Directory Structure
|
|
|
|
- [ ] Create `src/dto/profile-analytics/` directory
|
|
|
|
### 6.2 Extract Tracking DTOs
|
|
|
|
**File**: `src/dto/profile-analytics/tracking.dto.ts` (~250 lines)
|
|
|
|
- [ ] Create new file
|
|
- [ ] Copy lines 17-254 from original file:
|
|
- [ ] `DiscoverySourceContextDto`
|
|
- [ ] `TrackProfileDiscoveryDto`
|
|
- [ ] `TrackProfileDiscoveryBatchDto`
|
|
- [ ] `TrackProfileViewDto`
|
|
- [ ] `TrackPhotoViewDto`
|
|
- [ ] `ProfileEngagementType` enum
|
|
- [ ] `TrackProfileEngagementDto`
|
|
|
|
### 6.3 Extract Query DTOs
|
|
|
|
**File**: `src/dto/profile-analytics/query.dto.ts` (~50 lines)
|
|
|
|
- [ ] Create new file
|
|
- [ ] Copy lines 260-300:
|
|
- [ ] `DateRange` type
|
|
- [ ] `ProfileAnalyticsQueryDto`
|
|
- [ ] `ProfileChartQueryDto`
|
|
|
|
### 6.4 Extract Response Types
|
|
|
|
**File**: `src/dto/profile-analytics/responses.dto.ts` (~150 lines)
|
|
|
|
- [ ] Create new file
|
|
- [ ] Copy lines 305-428:
|
|
- [ ] `TrendDirection` type
|
|
- [ ] `MetricWithTrend` interface
|
|
- [ ] `ProfileAnalyticsOverview` interface
|
|
- [ ] `ChartDataPoint` interface
|
|
- [ ] `ProfileChartResponse` interface
|
|
- [ ] `DuoReferralSummary` interface
|
|
- [ ] `DuoReferralsResponse` interface
|
|
- [ ] `FunnelStep` interface
|
|
- [ ] `ProfileFunnelResponse` interface
|
|
- [ ] `MessageSourceBreakdown` interface
|
|
|
|
### 6.5 Create Barrel Export
|
|
|
|
**File**: `src/dto/profile-analytics/index.ts`
|
|
|
|
- [ ] Create barrel export:
|
|
```typescript
|
|
export * from './tracking.dto'
|
|
export * from './query.dto'
|
|
export * from './responses.dto'
|
|
```
|
|
|
|
### 6.6 Update Imports Across Codebase
|
|
|
|
- [ ] Find all imports of `profile-analytics.dto`:
|
|
```bash
|
|
grep -r "from '@/dto/profile-analytics.dto'" src/
|
|
```
|
|
- [ ] Update to new path:
|
|
```typescript
|
|
// Old
|
|
import { TrackProfileDiscoveryDto } from '@/dto/profile-analytics.dto'
|
|
|
|
// New
|
|
import { TrackProfileDiscoveryDto } from '@/dto/profile-analytics'
|
|
```
|
|
- [ ] Files to update (likely):
|
|
- [ ] `profile-tracking.controller.ts`
|
|
- [ ] `profile-analytics-facade.service.ts`
|
|
- [ ] `profile-event-tracker.service.ts`
|
|
- [ ] `profile-metrics-query.service.ts`
|
|
|
|
### 6.7 Delete Original File
|
|
|
|
- [ ] Delete `src/dto/profile-analytics.dto.ts`
|
|
|
|
### 6.8 Final Verification
|
|
|
|
- [ ] Run build: `pnpm build`
|
|
- [ ] Verify no import errors
|
|
- [ ] Run full test suite
|
|
- [ ] Check TypeScript compiler output (no errors)
|
|
|
|
---
|
|
|
|
## Post-Implementation Verification
|
|
|
|
### Code Quality Checks
|
|
|
|
- [ ] Run linter: `pnpm lint`
|
|
- [ ] Fix all linting warnings
|
|
- [ ] Run prettier: `pnpm format`
|
|
- [ ] Check for unused imports: `pnpm lint:unused-imports`
|
|
|
|
### Test Coverage
|
|
|
|
- [ ] Run test coverage report: `pnpm test:cov`
|
|
- [ ] Verify overall coverage ≥ 80%
|
|
- [ ] Verify new services coverage ≥ 80%
|
|
- [ ] Review uncovered lines (should be minimal)
|
|
|
|
### Build Verification
|
|
|
|
- [ ] Clean build: `rm -rf dist && pnpm build`
|
|
- [ ] Verify no TypeScript errors
|
|
- [ ] Check bundle size (should be ≤ 5% increase)
|
|
- [ ] Verify tree-shaking working (check bundle analysis)
|
|
|
|
### Runtime Verification
|
|
|
|
- [ ] Start dev server: `pnpm dev`
|
|
- [ ] Verify no startup errors
|
|
- [ ] Check all CRON jobs scheduled correctly
|
|
- [ ] Test tracking endpoints manually
|
|
- [ ] Test query endpoints manually
|
|
- [ ] Test admin endpoints manually
|
|
- [ ] Verify queue jobs processing
|
|
- [ ] Check logs for warnings/errors
|
|
|
|
### Documentation Updates
|
|
|
|
- [ ] Update architecture docs (if exists)
|
|
- [ ] Update developer guide (if needed)
|
|
- [ ] Add migration notes to CHANGELOG
|
|
- [ ] Update README (if service structure explained)
|
|
|
|
### Performance Testing
|
|
|
|
- [ ] Benchmark tracking endpoint latency (should be ≤ 50ms)
|
|
- [ ] Benchmark query endpoint latency (should be unchanged)
|
|
- [ ] Check memory usage (should be ≤ 5% increase)
|
|
- [ ] Verify Redis connection pooling working
|
|
|
|
### Final Sign-off
|
|
|
|
- [ ] Code review by team lead
|
|
- [ ] QA testing pass
|
|
- [ ] Merge to main branch
|
|
- [ ] Deploy to staging
|
|
- [ ] Monitor for errors (24 hours)
|
|
- [ ] Deploy to production
|
|
|
|
---
|
|
|
|
## Rollback Plan (If Issues Found)
|
|
|
|
### Emergency Rollback Steps
|
|
|
|
1. [ ] Identify issue and severity
|
|
2. [ ] Revert merge commit: `git revert <commit-hash>`
|
|
3. [ ] Deploy revert to production
|
|
4. [ ] Document issue in ticket
|
|
5. [ ] Fix in separate branch
|
|
6. [ ] Re-test thoroughly before re-deploying
|
|
|
|
### Known Rollback Triggers
|
|
|
|
- **Critical**: Tracking events lost (data integrity)
|
|
- **Critical**: CRON jobs failing (aggregation stopped)
|
|
- **High**: API endpoints returning 500 errors
|
|
- **High**: Test coverage drops below 70%
|
|
- **Medium**: Performance regression > 20%
|
|
- **Low**: Linting warnings
|
|
|
|
---
|
|
|
|
## Success Metrics
|
|
|
|
After completion, verify:
|
|
|
|
- [ ] **Line Count**: All files ≤ 400 lines (target: ≤ 350 avg)
|
|
- [ ] **Test Coverage**: ≥ 80% for all new services
|
|
- [ ] **API Compatibility**: 100% (no breaking changes)
|
|
- [ ] **Build Time**: ≤ 5% increase
|
|
- [ ] **Response Time**: ≤ 5% increase
|
|
- [ ] **Memory Usage**: ≤ 10% increase
|
|
- [ ] **Bundle Size**: ≤ 5% increase
|
|
- [ ] **Zero Downtime**: No service interruption during deployment
|
|
|
|
---
|
|
|
|
## Notes
|
|
|
|
- Estimated total time: 16-23 hours (2-3 days)
|
|
- Can be parallelized across team members
|
|
- Phase 1 (ProfileAnalytics) is critical path - do first
|
|
- Phases 2-6 can be done in parallel after Phase 1
|
|
- Each phase is independently testable
|
|
- Rollback possible at any phase boundary
|
|
|
|
**Status**: Not Started
|
|
**Last Updated**: 2026-01-22
|