# 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` - [ ] `trackDiscoveryBatch(inputs: TrackDiscoveryInput[]): Promise` - [ ] `trackProfileView(input: TrackProfileViewInput): Promise` - [ ] `trackPhotoView(input: TrackPhotoViewInput): Promise` - [ ] `trackEngagement(input: TrackEngagementInput): Promise` - [ ] 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` - [ ] `getProfileChart(profileId, dateRange, metric): Promise` - [ ] `getDuoPartnerReferrals(profileId, dateRange): Promise` - [ ] `getConversionFunnel(profileId, dateRange): Promise` - [ ] `getMessageSourceBreakdown(profileId, dateRange): Promise` - [ ] 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` - [ ] `private aggregateDuoReferrals(startOfDay, endOfDay): Promise` - [ ] 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` - [ ] `trackRevenue(dto: TrackRevenueDto): Promise` - [ ] `trackEngagement(dto: TrackEngagementEvent): Promise` - [ ] `trackInteraction(dto: TrackInteractionDto): Promise` - [ ] `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` - [ ] `getContentAnalytics(contentId): Promise` - [ ] Extract helper methods (lines 467-542): - [ ] `private getViewMetrics(userId, startDate, endDate): Promise` - [ ] `private getRevenueMetrics(userId, startDate, endDate): Promise` - [ ] `private getEngagementMetrics(userId, startDate, endDate): Promise` - [ ] Make `calculateMetrics()` public (needed by AggregationService): - [ ] `calculateMetrics(userId, startDate, endDate): Promise` **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` - [ ] `getAnalyticsOverview(userId): Promise` - [ ] `getRevenueChart(userId, period): Promise` - [ ] `getSubscriberChart(userId, period): Promise` - [ ] `getTopContentForUser(userId, limit): Promise` - [ ] `getConversionFunnel(userId): Promise` - [ ] `getActivityHeatmap(userId): Promise` - [ ] 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` - [ ] Extract helper (lines 581-588): - [ ] `private getCreatorIds(): Promise` - [ ] 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` - [ ] `private processView(data: ViewJobData): Promise` - [ ] `private processEngagement(data: EngagementJobData): Promise` - [ ] `private processRevenue(data: RevenueJobData): Promise` - [ ] 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` - [ ] `private aggregateDaily(data): Promise` - [ ] `private aggregateHourly(data): Promise` - [ ] Extract helper (lines 390-446): - [ ] `private calculateUserMetrics(userId, startDate, endDate): Promise` - [ ] 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): Promise { 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` - [ ] `trackClick(providerId, userId, searchRegion): Promise` - [ ] `trackBooking(providerId, userId, revenueCents, searchRegion): Promise` - [ ] Extract helper (lines 416-453): - [ ] `private incrementMetric(metricType, sourceRegion, providerRegion, count?, revenue?): Promise` **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` - [ ] `getComparison(period): Promise` - [ ] `getFmtyTrends(period): Promise` - [ ] `getTopRoutes(period, limit): Promise` - [ ] 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 ` 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