docs: consolidate exploration & audit reports under docs/ (TEC-3094)
- Move 8 stray .md (+5 .txt) from ~/Desktop into docs/explorations/from-desktop/ - Reorganize 27 .md/.txt at workspace root: - audit reports -> docs/audits/ - exploration reports -> docs/explorations/ - design system -> docs/design-system/ - Keep only README/CHANGELOG/CONTRIBUTING/CLAUDE at repo root - Refresh docs/README.md as canonical index with links to all groups - Note: pre-existing docs/audits/AUDIT_INDEX.md and AUDIT_SUMMARY.md were overwritten by the newer root-level versions during the move Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
217
docs/explorations/ANALYTICS_ARCHITECTURE_DIAGRAM.txt
Normal file
217
docs/explorations/ANALYTICS_ARCHITECTURE_DIAGRAM.txt
Normal file
@@ -0,0 +1,217 @@
|
||||
╔═════════════════════════════════════════════════════════════════════════════════════╗
|
||||
║ GOODGO ANALYTICS MODULE - ARCHITECTURE OVERVIEW ║
|
||||
╚═════════════════════════════════════════════════════════════════════════════════════╝
|
||||
|
||||
┌─────────────────────────────────────────────────────────────────────────────────────┐
|
||||
│ HTTP CLIENT │
|
||||
│ ├─ GET /analytics/market-report?city=... │
|
||||
│ ├─ GET /analytics/price-trend?district=... │
|
||||
│ ├─ POST /analytics/valuation (form body) │
|
||||
│ └─ GET /avm/explain?valuationId=... │
|
||||
└─────────────────────────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────────────────────────────┐
|
||||
│ PRESENTATION LAYER │
|
||||
│ ┌──────────────────────────────────────────────────────────────────────────┐ │
|
||||
│ │ AnalyticsController AvmController │ │
|
||||
│ │ ├─ GET /market-report ├─ POST /batch │ │
|
||||
│ │ ├─ GET /price-trend ├─ GET /history/:id │ │
|
||||
│ │ ├─ GET /heatmap ├─ GET /compare │ │
|
||||
│ │ ├─ POST /valuation ├─ GET /explain │ │
|
||||
│ │ ├─ POST /valuation/batch └─ POST /industrial │ │
|
||||
│ │ └─ GET /neighborhoods/:d/score │ │
|
||||
│ └──────────────────────────────────────────────────────────────────────────┘ │
|
||||
│ (Request/Response DTOs) │
|
||||
│ GetMarketReportDto PredictValuationDto BatchValuationDto etc. │
|
||||
└─────────────────────────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
┌─────────────────────┼─────────────────────┐
|
||||
│ │ │
|
||||
▼ ▼ ▼
|
||||
┌───────────────────────────────────────────────────────────┐
|
||||
│ SHARED MIDDLEWARE & GUARDS (Global) │
|
||||
├───────────────────────────────────────────────────────────┤
|
||||
│ • EndpointRateLimitGuard (Redis sliding-window) │
|
||||
│ └─ Rate limit key: "rate:{strategy}:{id}:{path}" │
|
||||
│ • JwtAuthGuard (verify JWT token) │
|
||||
│ • QuotaGuard (check subscription quota) │
|
||||
│ • CorrelationIdMiddleware (trace ID injection) │
|
||||
│ • RequestLoggingMiddleware (audit logging) │
|
||||
│ • SanitizeInputMiddleware (input validation) │
|
||||
└───────────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────────────────────────────┐
|
||||
│ APPLICATION LAYER (CQRS Pattern) │
|
||||
│ ┌──────────────────────────────────┐ ┌────────────────────────────────────────┐ │
|
||||
│ │ QUERIES (Read Operations) │ │ COMMANDS (Write Operations) │ │
|
||||
│ │ │ │ │ │
|
||||
│ │ Query Classes & Handlers: │ │ • GenerateReportHandler │ │
|
||||
│ │ ├─ GetPriceTrendQuery │ │ • TrackEventHandler │ │
|
||||
│ │ ├─ GetHeatmapQuery │ │ • UpdateMarketIndexHandler │ │
|
||||
│ │ ├─ GetMarketReportQuery │ │ │ │
|
||||
│ │ ├─ GetDistrictStatsQuery │ │ EVENT HANDLERS: │ │
|
||||
│ │ ├─ GetValuationQuery │ │ • ListingCreatedModerationHandler │ │
|
||||
│ │ ├─ PredictValuationQuery │ │ │ │
|
||||
│ │ ├─ BatchValuationQuery │ │ QUERYBUS.EXECUTE() │ │
|
||||
│ │ ├─ IndustrialValuationQuery │ │ COMMANDBUS.EXECUTE() │ │
|
||||
│ │ ├─ GetNeighborhoodScoreQuery │ │ EVENTBUS.EMIT() │ │
|
||||
│ │ ├─ GetNearbyPOIsQuery │ │ │ │
|
||||
│ │ ├─ GetListingAiAdviceQuery │ │ │ │
|
||||
│ │ ├─ GetProjectAiAdviceQuery │ │ │ │
|
||||
│ │ ├─ ValuationHistoryQuery │ │ │ │
|
||||
│ │ ├─ ValuationComparisonQuery │ │ │ │
|
||||
│ │ └─ ValuationExplanationQuery │ │ │ │
|
||||
│ └──────────────────────────────────┘ └────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ All handlers follow this pattern: │
|
||||
│ 1. Build cache key with CacheService.buildKey(CachePrefix.*, ...params) │
|
||||
│ 2. Call cache.getOrSet(cacheKey, loader, CacheTTL.*, 'metric_label') │
|
||||
│ 3. Catch DomainException separately; wrap others │
|
||||
│ 4. Return DTO from handler │
|
||||
└─────────────────────────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────────────────────────────┐
|
||||
│ DOMAIN LAYER (DDD Pattern) │
|
||||
│ ┌────────────────────────────────────────────────────────────────────────────┐ │
|
||||
│ │ REPOSITORY INTERFACES (Abstraction) │ │
|
||||
│ │ • IMarketIndexRepository │ │
|
||||
│ │ • IValuationRepository │ │
|
||||
│ │ │ │
|
||||
│ │ DOMAIN SERVICES (Business Logic) │ │
|
||||
│ │ • IAVMService (interface) │ │
|
||||
│ │ • INeighborhoodScoreService (interface) │ │
|
||||
│ │ │ │
|
||||
│ │ DOMAIN ENTITIES │ │
|
||||
│ │ • MarketIndexEntity │ │
|
||||
│ │ • ValuationEntity │ │
|
||||
│ └────────────────────────────────────────────────────────────────────────────┘ │
|
||||
└─────────────────────────────────────────────────────────────────────────────────────┘
|
||||
│ │ │
|
||||
▼ ▼ ▼
|
||||
┌────────────────────┐ ┌────────────────────────┐ ┌─────────────────────────┐
|
||||
│ INFRASTRUCTURE │ │ REDIS CACHE SERVICE │ │ PRISMA REPOSITORIES │
|
||||
├────────────────────┤ ├────────────────────────┤ ├─────────────────────────┤
|
||||
│ • HttpAVMService │ │ cache.getOrSet() │ │ PrismaMarketIndexRepo │
|
||||
│ (→ Python AI) │ │ │ │ PrismaValuationRepo │
|
||||
│ │ │ Metrics: │ │ │
|
||||
│ • PrismaAVMService │ │ • cache_hit_total │ │ Converts: │
|
||||
│ (fallback) │ │ • cache_miss_total │ │ Prisma → Domain Entity │
|
||||
│ │ │ • cache_degradation │ │ │
|
||||
│ • HttpNbScore │ │ │ │ Query patterns: │
|
||||
│ Service │ │ Cache Prefixes: │ │ • findById │
|
||||
│ (→ Python) │ │ • cache:market:trend │ │ • findMany │
|
||||
│ │ │ • cache:market:report │ │ • getMarketReport │
|
||||
│ • PrismaNeighbor │ │ • cache:market:heatmap │ │ • getHeatmap │
|
||||
│ Score Service │ │ • cache:valuation │ │ • getPriceTrend │
|
||||
│ (fallback) │ │ │ │ • getDistrictStats │
|
||||
│ │ │ TTLs: │ │ │
|
||||
│ • AiServiceClient │ │ • MARKET_DATA: 1800s │ │ All use PrismaService │
|
||||
│ (Claude API) │ │ • MARKET_REPORT: 900s │ │ for database access │
|
||||
│ │ │ • HEATMAP: 300s │ │ │
|
||||
│ • MarketIndex │ │ • DISTRICT_STATS: 300s │ │ │
|
||||
│ CronService │ │ • REFERENCE_DATA: 86400s │ │ │
|
||||
└────────────────────┘ └────────────────────────┘ └─────────────────────────┘
|
||||
│ │ │
|
||||
│ │ │
|
||||
│ ┌────────────┴───────────┐ │
|
||||
│ ▼ ▼ │
|
||||
│ ┌──────────────────┐ ┌───────────────────┐ │
|
||||
│ │ REDIS │ │ POSTGRESQL 16 │ │
|
||||
│ ├──────────────────┤ ├───────────────────┤ │
|
||||
│ │ Sliding Window │ │ Tables: │ │
|
||||
│ │ Rate Limiter │ │ • Property │ │
|
||||
│ │ │ │ • Listing │ │
|
||||
│ │ Cache Storage │ │ • PriceHistory │ │
|
||||
│ │ (cache-aside) │ │ • MarketIndex │ │
|
||||
│ │ │ │ • Valuation │ │
|
||||
│ │ Metrics: │ │ • User │ │
|
||||
│ │ rate:*:*:* │ │ • Subscription │ │
|
||||
│ └──────────────────┘ │ + PostGIS │ │
|
||||
│ │ (geometry) │ │
|
||||
│ └───────────────────┘ │
|
||||
│ │
|
||||
└────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌──────────────────────────────────────┐
|
||||
│ EXTERNAL SERVICES │
|
||||
├──────────────────────────────────────┤
|
||||
│ • Python AI Service │
|
||||
│ (AVM, Neighborhood Score) │
|
||||
│ • Anthropic Claude API │
|
||||
│ (Listing/Project AI Advice) │
|
||||
│ • Google Maps/OSM API │
|
||||
│ (Nearby POIs) │
|
||||
└──────────────────────────────────────┘
|
||||
|
||||
|
||||
╔═════════════════════════════════════════════════════════════════════════════════════╗
|
||||
║ DATA FLOW EXAMPLE ║
|
||||
║ GET /analytics/price-trend?district=... ║
|
||||
╚═════════════════════════════════════════════════════════════════════════════════════╝
|
||||
|
||||
1. Controller receives query DTO
|
||||
└─ @Query() dto: GetPriceTrendDto
|
||||
|
||||
2. Controller validates & creates Query object
|
||||
└─ new GetPriceTrendQuery(dto.district, dto.city, dto.propertyType, dto.periods)
|
||||
|
||||
3. Controller sends to QueryBus
|
||||
└─ queryBus.execute(query)
|
||||
|
||||
4. QueryBus routes to GetPriceTrendHandler
|
||||
|
||||
5. Handler builds cache key
|
||||
└─ CacheService.buildKey(CachePrefix.MARKET_TREND, district, city, propertyType, periods)
|
||||
└─ Result: "cache:market:trend:Quận 1:Hồ Chí Minh:APARTMENT:2024-Q1,2024-Q2"
|
||||
|
||||
6. Handler calls cache.getOrSet()
|
||||
a) Redis lookup → FOUND? Return cached data
|
||||
b) MISS? Call loader function:
|
||||
• Call marketIndexRepo.getPriceTrend()
|
||||
• PrismaMarketIndexRepository queries PostgreSQL
|
||||
• Fetch: SELECT * FROM "MarketIndex" WHERE district=? AND city=? AND ...
|
||||
• Convert Prisma model → DomainEntity
|
||||
• Return trend data
|
||||
c) Store in Redis with TTL (1800s = 30 min)
|
||||
d) Return data to caller
|
||||
|
||||
7. Handler returns PriceTrendDto to controller
|
||||
|
||||
8. Controller returns JSON to client
|
||||
|
||||
9. Response includes:
|
||||
• Cached status (if applicable)
|
||||
• Rate-limit headers (X-RateLimit-*)
|
||||
• CorrelationId (for tracing)
|
||||
• Standard error format (if error)
|
||||
|
||||
|
||||
╔═════════════════════════════════════════════════════════════════════════════════════╗
|
||||
║ SHARED UTILITIES & EXPORTS ║
|
||||
╚═════════════════════════════════════════════════════════════════════════════════════╝
|
||||
|
||||
From @modules/shared:
|
||||
• CacheService
|
||||
• CachePrefix (enum)
|
||||
• CacheTTL (const)
|
||||
• RedisService
|
||||
• LoggerService
|
||||
• PrismaService
|
||||
• DomainException & subclasses
|
||||
• EndpointRateLimit (decorator)
|
||||
• EndpointRateLimitGuard
|
||||
• ErrorResponseBody interface
|
||||
• JwtAuthGuard
|
||||
• QuotaGuard
|
||||
• @RequireQuota decorator
|
||||
|
||||
Exports from analytics.module.ts:
|
||||
• MARKET_INDEX_REPOSITORY
|
||||
• VALUATION_REPOSITORY
|
||||
• AVM_SERVICE
|
||||
• AI_SERVICE_CLIENT
|
||||
|
||||
Reference in New Issue
Block a user