platform-codebase/features/analytics/backend-api/REFACTORING-CHECKLIST.md

32 KiB

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:
    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:
    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:
    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:
    // 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:
    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:
    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:
    controllers: [
      AdminRevenueController,
      AdminPlatformController,
      AdminConversionController,
      AdminFeaturesController,
      // ... other controllers
    ]
    

2.7 Update Controller Index Export

File: src/controllers/index.ts

  • Remove old export
  • Add new exports:
    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:
    constructor(
      private eventHandler: AnalyticsEventHandler,
      private aggregationHandler: AnalyticsAggregationHandler,
    ) { super() }
    
  • Simplify process() method:
    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:
    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:
    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:
    grep -r "from '@/dto/profile-analytics.dto'" src/
    
  • Update to new path:
    // 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