# Analytics Module Exploration - Summary Report ## ✅ Exploration Complete I've thoroughly explored the GoodGo Platform analytics module and created comprehensive documentation. Here's what was analyzed: --- ## 📄 Documentation Created 1. **ANALYTICS_ARCHITECTURE.md** (Comprehensive, 10 sections) - Full module structure with DDD/CQRS layers - Controller & endpoint mapping - Query/handler CQRS pattern deep-dive - Redis caching patterns with Lua scripts - Prisma schema for Property, Listing, MarketIndex, Valuation models - Shared guards, decorators, and exception patterns - DTO patterns and module dependency injection - Key patterns and quick reference paths 2. **ANALYTICS_QUICK_REFERENCE.md** (Developer-friendly reference) - Quick architecture overview - File paths and module organization - Guard & decorator stack with usage examples - Cache patterns (cache-aside, TTLs, prefixes) - Prisma models summary - CQRS handler pattern code example - Error handling pattern - Common endpoints list - Dependency injection patterns - Key conventions table 3. **ANALYTICS_ARCHITECTURE_DIAGRAM.txt** (Visual reference) - Complete system architecture diagram - Data flow walkthrough for example request - All layers from HTTP → Database - External service integrations - Shared utilities & exports --- ## 🎯 Key Findings ### 1. Architecture Pattern: **DDD + CQRS** - **Presentation**: Controllers (analytics, avm) + DTOs - **Application**: Query/Command handlers with Prometheus metrics - **Domain**: Repository interfaces, service abstractions, entities - **Infrastructure**: Prisma repositories, external service clients ### 2. Controllers (2 total) - `AnalyticsController` → `/analytics/*` (13+ endpoints) - `AvmController` → `/avm/*` (5 endpoints) ### 3. Query Handlers (14+) All follow cache-aside pattern: - Price trends, heatmaps, market reports, district stats - Valuations (single, batch, history, comparison, explanation) - Industrial valuation - Neighborhood scores, nearby POIs - AI advice (Claude integration for listings/projects) ### 4. Redis Caching Strategy **Pattern**: Cache-aside with graceful degradation ``` cache.getOrSet(key, loader, TTL, metricLabel) ``` **TTLs for analytics**: - MARKET_DATA: 1800s (30 min) - price trends - MARKET_REPORT: 900s (15 min) - summaries - HEATMAP: 300s (5 min) - tiles - DISTRICT_STATS: 300s (5 min) - statistics **Cache Prefixes**: ``` cache:market:report cache:market:trend cache:market:heatmap cache:market:district cache:valuation ``` ### 5. Rate Limiting (Redis Sliding-Window) **Guard**: `EndpointRateLimitGuard` **Decorator**: `@EndpointRateLimit({ limit, windowSeconds, keyStrategy })` **Implementation**: Lua script with sorted set (ZSET) in Redis **Strategy**: `'user'` (by user ID) or `'ip'` (by client IP) Example: ```typescript @EndpointRateLimit({ limit: 10, windowSeconds: 60, keyStrategy: 'user' }) @UseGuards(EndpointRateLimitGuard, JwtAuthGuard, QuotaGuard) ``` ### 6. Guards Stack (Order Matters) 1. `EndpointRateLimitGuard` → Redis rate limit 2. `JwtAuthGuard` → JWT verification 3. `QuotaGuard` → Subscription quota check ### 7. Prisma Schema - Key Models **Property**: - propertyType (APARTMENT, HOUSE, LAND, COMMERCIAL) - status (ACTIVE, SOLD, RENTED, REMOVED) - district, city, location (PostGIS geometry) - areaM2, bedrooms, bathrooms, yearBuilt, etc. **Listing** (analytics-aware): - priceVND (BigInt, checked > 0) - pricePerM2 (float, derived for analytics) - aiPriceEstimate, aiConfidence (AVM fields) - viewCount, saveCount, inquiryCount (tracking) - publishedAt, createdAt, status **MarketIndex** (pre-calculated): - medianPrice, avgPriceM2, totalListings - daysOnMarket, inventoryLevel, absorptionRate - yoyChange (year-over-year) - Unique index: (district, city, propertyType, period) **Valuation** (AVM storage): - estimatedPrice, confidence - drivers (JSON), comparables (JSON) - explanation, model version ### 8. Repository Pattern **Interface** (domain): ```typescript export const MARKET_INDEX_REPOSITORY = Symbol('MARKET_INDEX_REPOSITORY'); export interface IMarketIndexRepository { findById(id: string): Promise; getMarketReport(...): Promise; getPriceTrend(...): Promise; getHeatmap(...): Promise; getDistrictStats(...): Promise; } ``` **Implementation** (infrastructure): ```typescript @Injectable() export class PrismaMarketIndexRepository implements IMarketIndexRepository { constructor(private readonly prisma: PrismaService) {} // Converts Prisma → Domain entities } ``` **Injection** (handler): ```typescript constructor( @Inject(MARKET_INDEX_REPOSITORY) private readonly repo: IMarketIndexRepository ) {} ``` ### 9. Error Handling Pattern ```typescript try { return this.cache.getOrSet(...); } catch (error) { if (error instanceof DomainException) throw error; // Re-throw this.logger.error(...); // Log with context throw new InternalServerErrorException('...'); // Wrap & return user message } ``` ### 10. Shared Module Exports From `@modules/shared`: - **CacheService** with `getOrSet()` method - **RedisService** connection pool - **LoggerService** structured logging - **PrismaService** database access - **DomainException** & subclasses - **EndpointRateLimit** decorator - **EndpointRateLimitGuard** guard - **JwtAuthGuard**, **QuotaGuard** - Error response standardization via GlobalExceptionFilter ### 11. DTO Conventions - **Request DTOs**: Query parameters, body validation - **Response DTOs**: Defined as handler return type interfaces - **BigInt handling**: Always stringified for JSON safety (`.toString()`) --- ## 🔗 File Path Quick Map ``` ANALYTICS ROOT └── apps/api/src/modules/analytics/ CONTROLLERS ├── presentation/controllers/analytics.controller.ts (13+ endpoints) └── presentation/controllers/avm.controller.ts (5 endpoints) QUERY HANDLERS (14+ total) ├── application/queries/get-price-trend/ ← Cache-aside pattern ├── application/queries/get-heatmap/ ├── application/queries/get-market-report/ ├── application/queries/get-valuation/ ├── application/queries/predict-valuation/ ├── application/queries/batch-valuation/ ├── application/queries/valuation-history/ ├── application/queries/valuation-comparison/ ├── application/queries/valuation-explanation/ ├── application/queries/get-neighborhood-score/ ├── application/queries/get-nearby-pois/ ├── application/queries/get-listing-ai-advice/ (Claude) └── application/queries/get-project-ai-advice/ (Claude) COMMAND HANDLERS (3) ├── application/commands/generate-report/ ├── application/commands/track-event/ └── application/commands/update-market-index/ EVENT HANDLERS (1) └── application/event-handlers/listing-created-moderation.handler.ts REPOSITORIES ├── domain/repositories/market-index.repository.ts (interface) ├── domain/repositories/valuation.repository.ts (interface) ├── infrastructure/repositories/prisma-market-index.repository.ts └── infrastructure/repositories/prisma-valuation.repository.ts SERVICES ├── infrastructure/services/http-avm.service.ts (→ Python AI) ├── infrastructure/services/prisma-avm.service.ts (fallback) ├── infrastructure/services/http-neighborhood-score.service.ts ├── infrastructure/services/prisma-neighborhood-score.service.ts ├── infrastructure/services/ai-service.client.ts (Claude) └── infrastructure/services/market-index-cron.service.ts SHARED MODULE (Global) └── apps/api/src/modules/shared/ ├── infrastructure/ │ ├── cache.service.ts ← Cache-aside │ ├── redis.service.ts ← Connection pool │ ├── logger.service.ts ← Structured logging │ ├── prisma.service.ts ← Database │ ├── guards/endpoint-rate-limit.guard.ts ← Lua sliding-window │ ├── decorators/endpoint-rate-limit.decorator.ts │ ├── middleware/correlation-id.middleware.ts ← Trace ID │ └── filters/global-exception.filter.ts ← Error standardization └── domain/ ├── domain-exception.ts ← Exception base └── error-codes.ts ``` --- ## 💡 Implementation Patterns to Follow ### 1. New Query Handler Template ```typescript @QueryHandler(YourQuery) export class YourQueryHandler implements IQueryHandler { constructor( @Inject(YOUR_REPOSITORY) private readonly repo: IYourRepository, private readonly cache: CacheService, private readonly logger: LoggerService, ) {} async execute(query: YourQuery): Promise { try { const cacheKey = CacheService.buildKey( CachePrefix.YOUR_PREFIX, query.param1, query.param2, ); return this.cache.getOrSet( cacheKey, async () => { const data = await this.repo.yourMethod(...); return { ...data }; }, CacheTTL.YOUR_TTL, 'your_metric_label', ); } catch (error) { if (error instanceof DomainException) throw error; this.logger.error(`Failed to ...`, error?.stack, this.constructor.name); throw new InternalServerErrorException('User-friendly message'); } } } ``` ### 2. New Controller Endpoint Template ```typescript @ApiBearerAuth('JWT') @EndpointRateLimit({ limit: 10, windowSeconds: 60, keyStrategy: 'user' }) @UseGuards(EndpointRateLimitGuard, JwtAuthGuard, QuotaGuard) @RequireQuota('analytics_queries') @Get('your-endpoint') @ApiOperation({ summary: 'Description' }) async yourMethod(@Query() dto: YourDto): Promise { return this.queryBus.execute( new YourQuery(dto.param1, dto.param2, ...) ); } ``` ### 3. Register Handler in Module ```typescript const QueryHandlers = [ // existing handlers... YourQueryHandler, ]; @Module({ providers: [ ...QueryHandlers, ], }) ``` --- ## 🚀 Next Steps for Implementation When building new analytics features: 1. **Define Cache Strategy** - Choose TTL from `CacheTTL.*` or create new one - Use appropriate `CachePrefix.*` 2. **Create Query & Handler** - Query class: simple data holder - Handler: cache-aside + repository call 3. **Define DTO** - Request DTO for controller parameters - Response DTO as handler return type 4. **Add Controller Endpoint** - Use guard stack: `EndpointRateLimitGuard` → `JwtAuthGuard` → `QuotaGuard` - Call `queryBus.execute()` 5. **Register in Module** - Add handler to `QueryHandlers` array - Add to module `providers` 6. **Error Handling** - Catch and rethrow `DomainException` - Log unexpected errors with context - Return user-friendly message --- ## 📊 Statistics - **Controllers**: 2 - **Query Handlers**: 14+ - **Command Handlers**: 3 - **Event Handlers**: 1 - **DTOs**: 15+ - **Repositories**: 2 interfaces + 2 implementations - **Cache Prefixes**: 12 - **TTLs Configured**: 10+ - **Endpoints**: 18+ - **Rate Limit Configurations**: Multiple per endpoint --- ## 🎓 Architecture Highlights ✅ **DDD Layers**: Clear separation of concerns ✅ **CQRS Pattern**: Query/Command handlers with event sourcing capability ✅ **Cache-Aside Pattern**: Redis caching with graceful degradation ✅ **Sliding-Window Rate Limiting**: Accurate per-endpoint limiting with Redis ✅ **Dependency Injection**: Repository pattern with interface abstraction ✅ **Error Standardization**: Global exception filter with error codes ✅ **Prometheus Metrics**: Cache hit/miss/degradation tracking ✅ **Middleware Stack**: Correlation ID, audit logging, CSRF, input sanitization ✅ **External Service Fallback**: HTTP → Python AI with Prisma fallback ✅ **Quota Management**: Subscription-based quota enforcement per resource --- **All documentation files saved to project root:** - `ANALYTICS_ARCHITECTURE.md` - Comprehensive reference - `ANALYTICS_QUICK_REFERENCE.md` - Developer quick guide - `ANALYTICS_ARCHITECTURE_DIAGRAM.txt` - Visual overview - `EXPLORATION_SUMMARY.md` - This file