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:
384
docs/explorations/from-desktop/03_file_paths_reference.md
Normal file
384
docs/explorations/from-desktop/03_file_paths_reference.md
Normal file
@@ -0,0 +1,384 @@
|
||||
# Analytics Module — File Paths & Quick Reference
|
||||
|
||||
## 🔗 Core Module Files
|
||||
|
||||
```
|
||||
/apps/api/src/modules/analytics/
|
||||
│
|
||||
├── analytics.module.ts
|
||||
│ └─ Registers all handlers, repositories, services
|
||||
│ └─ Module metadata: imports, controllers, providers, exports
|
||||
│ └─ KEY: CommandHandlers, QueryHandlers, EventHandlers arrays
|
||||
│
|
||||
├── index.ts
|
||||
│ └─ Public exports of analytics module
|
||||
│
|
||||
└── README.md
|
||||
└─ Module documentation
|
||||
```
|
||||
|
||||
## 🎯 Controllers (Entry Points)
|
||||
|
||||
```
|
||||
/apps/api/src/modules/analytics/presentation/controllers/
|
||||
|
||||
analytics.controller.ts (19 endpoints)
|
||||
├─ GET /analytics/market-report
|
||||
├─ GET /analytics/market-snapshot
|
||||
├─ GET /analytics/price-trend
|
||||
├─ GET /analytics/heatmap
|
||||
├─ GET /analytics/district-stats
|
||||
├─ GET /analytics/valuation (query param)
|
||||
├─ POST /analytics/valuation (body)
|
||||
├─ POST /analytics/valuation/batch
|
||||
├─ GET /analytics/valuation/history/:propertyId
|
||||
├─ POST /analytics/valuation/compare
|
||||
├─ GET /analytics/neighborhoods/:district/score
|
||||
├─ GET /analytics/pois/nearby
|
||||
├─ POST /analytics/listings/:id/ai-advice
|
||||
└─ POST /analytics/projects/:id/ai-advice
|
||||
|
||||
avm.controller.ts (5 endpoints)
|
||||
├─ POST /avm/batch
|
||||
├─ GET /avm/history/:propertyId
|
||||
├─ GET /avm/compare?ids=...
|
||||
├─ GET /avm/explain?valuationId=...
|
||||
└─ POST /avm/industrial
|
||||
```
|
||||
|
||||
## 📋 DTOs (Requests & Responses)
|
||||
|
||||
```
|
||||
/apps/api/src/modules/analytics/presentation/dto/
|
||||
|
||||
REQUEST DTOs (from @Query/@Body):
|
||||
├─ get-district-stats.dto.ts (city, period)
|
||||
├─ get-heatmap.dto.ts (city, period)
|
||||
├─ get-market-report.dto.ts (city, period, propertyType)
|
||||
├─ get-market-snapshot.dto.ts (city, propertyType)
|
||||
├─ get-price-trend.dto.ts (district, city, propertyType, periods)
|
||||
├─ get-valuation.dto.ts (propertyId | lat/lng/areaM2)
|
||||
├─ get-nearby-pois.dto.ts (lat, lng, radius, limit)
|
||||
├─ predict-valuation.dto.ts (20+ fields, v1 & v2)
|
||||
├─ batch-valuation.dto.ts (propertyIds: string[])
|
||||
├─ valuation-history.dto.ts (limit)
|
||||
├─ valuation-comparison.dto.ts (propertyIds)
|
||||
├─ avm-compare-query.dto.ts (ids)
|
||||
├─ avm-explain-query.dto.ts (valuationId)
|
||||
├─ industrial-valuation.dto.ts (30+ industrial fields)
|
||||
└─ get-trending-areas.dto.ts (city, propertyType, limit, period)
|
||||
|
||||
RESPONSE DTOs (exported from handlers):
|
||||
(See handler files below)
|
||||
```
|
||||
|
||||
## 🔄 Queries (CQRS Pattern)
|
||||
|
||||
```
|
||||
/apps/api/src/modules/analytics/application/queries/
|
||||
|
||||
STRUCTURE OF EACH QUERY TYPE:
|
||||
get-market-snapshot/
|
||||
├─ get-market-snapshot.query.ts
|
||||
│ └─ export class GetMarketSnapshotQuery { ... }
|
||||
│
|
||||
└─ get-market-snapshot.handler.ts
|
||||
├─ @QueryHandler(GetMarketSnapshotQuery)
|
||||
├─ execute(query): Promise<MarketSnapshotDto>
|
||||
└─ export interface MarketSnapshotDto { ... }
|
||||
|
||||
ALL QUERY TYPES (15+):
|
||||
├─ get-market-snapshot/ ..................... Dashboard overview
|
||||
├─ get-district-stats/ ..................... Stats aggregation
|
||||
├─ get-price-trend/ ........................ Time-series data
|
||||
├─ get-heatmap/ ........................... Geographic visualization
|
||||
├─ get-valuation/ ......................... Single valuation
|
||||
├─ predict-valuation/ ..................... AI prediction
|
||||
├─ batch-valuation/ ....................... Multiple valuations
|
||||
├─ valuation-history/ ..................... Time-series valuations
|
||||
├─ valuation-comparison/ .................. Side-by-side comparison
|
||||
├─ valuation-explanation/ ................. Model drivers
|
||||
├─ get-neighborhood-score/ ................ Neighborhood quality
|
||||
├─ get-nearby-pois/ ....................... Point of interests
|
||||
├─ get-listing-ai-advice/ ................. Claude analysis
|
||||
├─ get-project-ai-advice/ ................. Project analysis
|
||||
├─ industrial-valuation/ .................. Industrial rent
|
||||
├─ get-market-report/ ..................... Detailed report
|
||||
└─ get-trending-areas/ .................... Trending districts
|
||||
```
|
||||
|
||||
## 🏛️ Domain Layer
|
||||
|
||||
```
|
||||
/apps/api/src/modules/analytics/domain/
|
||||
|
||||
repositories/ (Interfaces only — NO IMPLEMENTATION)
|
||||
├─ market-index.repository.ts
|
||||
│ ├─ export const MARKET_INDEX_REPOSITORY = Symbol(...)
|
||||
│ ├─ export interface IMarketIndexRepository {
|
||||
│ │ findById(id)
|
||||
│ │ findByKey(district, city, propertyType, period)
|
||||
│ │ save(entity)
|
||||
│ │ update(entity)
|
||||
│ │ getMarketReport(city, period, propertyType?)
|
||||
│ │ getHeatmap(city, period)
|
||||
│ │ getPriceTrend(district, city, propertyType, periods)
|
||||
│ │ getDistrictStats(city, period)
|
||||
│ │ }
|
||||
│ └─ Result interfaces: MarketReportResult, HeatmapDataPoint, etc.
|
||||
│
|
||||
└─ valuation.repository.ts
|
||||
├─ export const VALUATION_REPOSITORY = Symbol(...)
|
||||
└─ export interface IValuationRepository { ... }
|
||||
|
||||
entities/
|
||||
├─ market-index.entity.ts
|
||||
│ └─ Domain logic for market data aggregation
|
||||
│
|
||||
└─ valuation.entity.ts
|
||||
└─ Domain logic for property valuation
|
||||
|
||||
services/
|
||||
├─ avm-service.ts
|
||||
│ └─ export const AVM_SERVICE = Symbol(...)
|
||||
│ └─ IAVMService interface: predict(), getComparables(), etc.
|
||||
│
|
||||
└─ neighborhood-score.service.ts
|
||||
└─ INeighborhoodScoreService interface
|
||||
|
||||
events/
|
||||
└─ market-index-updated.event.ts
|
||||
└─ Domain event when market data updates
|
||||
```
|
||||
|
||||
## 🔧 Infrastructure Layer
|
||||
|
||||
```
|
||||
/apps/api/src/modules/analytics/infrastructure/
|
||||
|
||||
repositories/ (IMPLEMENTATIONS)
|
||||
├─ prisma-market-index.repository.ts
|
||||
│ ├─ @Injectable()
|
||||
│ └─ class PrismaMarketIndexRepository implements IMarketIndexRepository {
|
||||
│ └─ Uses PrismaService for data access
|
||||
│
|
||||
└─ prisma-valuation.repository.ts
|
||||
└─ class PrismaValuationRepository implements IValuationRepository
|
||||
|
||||
services/ (IMPLEMENTATIONS)
|
||||
├─ http-avm.service.ts
|
||||
│ ├─ @Injectable()
|
||||
│ ├─ Calls Python AI service (HTTP client)
|
||||
│ └─ Falls back to PrismaAVMService if Python is down
|
||||
│
|
||||
├─ prisma-avm.service.ts
|
||||
│ ├─ @Injectable()
|
||||
│ └─ Fallback ML model using Prisma data
|
||||
│
|
||||
├─ http-neighborhood-score.service.ts
|
||||
│ ├─ HTTP proxy to external scoring service
|
||||
│ └─ Falls back to PrismaNeighborhoodScoreService
|
||||
│
|
||||
├─ prisma-neighborhood-score.service.ts
|
||||
│ └─ In-DB scoring logic
|
||||
│
|
||||
├─ ai-service.client.ts
|
||||
│ ├─ Wrapper around Anthropic SDK
|
||||
│ └─ Calls Claude API for AI analysis
|
||||
│
|
||||
└─ market-index-cron.service.ts
|
||||
└─ Scheduled job to update MarketIndex table
|
||||
```
|
||||
|
||||
## 🎨 Interceptors
|
||||
|
||||
```
|
||||
/apps/api/src/modules/analytics/presentation/interceptors/
|
||||
|
||||
cache-meta.interceptor.ts
|
||||
├─ @Injectable() CacheMetaInterceptor
|
||||
├─ Wraps response: T => { data: T; cacheMeta: {...} }
|
||||
├─ cacheMeta includes: cachedAt, nextRefreshAt, source
|
||||
└─ Applied via @UseInterceptors(CacheMetaInterceptor)
|
||||
```
|
||||
|
||||
## 📦 Shared Module (Reusable Utilities)
|
||||
|
||||
```
|
||||
/apps/api/src/modules/shared/
|
||||
|
||||
infrastructure/
|
||||
|
||||
cache.service.ts
|
||||
├─ @Injectable() CacheService
|
||||
├─ async getOrSet<T>(key, loader, ttl, resource)
|
||||
│ └─ Cache-aside pattern
|
||||
│ └─ Metrics: cache_hit_total, cache_miss_total, cache_degradation_total
|
||||
├─ async invalidate(key)
|
||||
├─ async invalidateByPrefix(prefix)
|
||||
├─ static buildKey(prefix, ...parts)
|
||||
└─ Graceful degradation when Redis is down
|
||||
|
||||
decorators/
|
||||
├─ cacheable.decorator.ts
|
||||
│ ├─ @Cacheable(options) method decorator
|
||||
│ └─ Declarative caching for query handlers
|
||||
│
|
||||
└─ other decorators...
|
||||
|
||||
cache-meta.store.ts
|
||||
├─ export const cacheMetaStorage = new AsyncLocalStorage()
|
||||
└─ Per-request storage of cache metadata
|
||||
|
||||
logger.service.ts
|
||||
├─ @Injectable() LoggerService
|
||||
├─ log(), warn(), error() with context
|
||||
└─ Winston integration
|
||||
|
||||
prisma.service.ts
|
||||
├─ @Injectable() PrismaService
|
||||
├─ Wrapper around Prisma Client
|
||||
└─ Handles connection lifecycle
|
||||
|
||||
redis.service.ts
|
||||
├─ @Injectable() RedisService
|
||||
├─ get(), set(), del(), scan()
|
||||
└─ Health check & graceful degradation
|
||||
|
||||
guards/
|
||||
├─ endpoint-rate-limit.guard.ts
|
||||
├─ ... other guards
|
||||
└─ auth module exports JwtAuthGuard
|
||||
|
||||
shared.module.ts
|
||||
└─ Registers all shared services
|
||||
```
|
||||
|
||||
## 📊 Database Schema
|
||||
|
||||
```
|
||||
prisma/schema.prisma
|
||||
|
||||
Models relevant to analytics:
|
||||
├─ Property
|
||||
│ ├─ id, propertyType, address, district, city
|
||||
│ ├─ location (PostGIS Point)
|
||||
│ ├─ areaM2, bedrooms, bathrooms, floors
|
||||
│ └─ Indexes: [propertyType], [district, city], [location]
|
||||
│
|
||||
├─ Listing
|
||||
│ ├─ id, propertyId (FK), sellerId (FK)
|
||||
│ ├─ status (ACTIVE, SOLD, EXPIRED, ...)
|
||||
│ ├─ priceVND (BigInt), pricePerM2, publishedAt
|
||||
│ ├─ aiPriceEstimate, aiConfidence (for AVM)
|
||||
│ └─ Indexes: [status], [sellerId, status], [publishedAt]
|
||||
│
|
||||
├─ MarketIndex
|
||||
│ ├─ district, city, propertyType, period
|
||||
│ ├─ medianPrice (BigInt), avgPriceM2
|
||||
│ ├─ totalListings, daysOnMarket, inventoryLevel
|
||||
│ └─ Unique: [district, city, propertyType, period]
|
||||
│
|
||||
├─ Valuation
|
||||
│ ├─ id, propertyId (FK)
|
||||
│ ├─ estimatedPrice (BigInt), confidence
|
||||
│ ├─ method (AVM_v1, AVM_v2, MANUAL)
|
||||
│ ├─ features (Json), comparables (Json), explainers (Json)
|
||||
│ └─ Index: [propertyId, valuationDate DESC]
|
||||
│
|
||||
└─ ProjectDevelopment
|
||||
├─ id, slug, developer
|
||||
├─ location (PostGIS Point)
|
||||
├─ minPrice, maxPrice, pricePerM2Range
|
||||
└─ Index: [district, city], [location]
|
||||
```
|
||||
|
||||
## 🌳 Directory Tree Summary
|
||||
|
||||
```
|
||||
goodgo-platform-ai/
|
||||
└─ apps/api/src/modules/
|
||||
├─ analytics/ (this module) .................. ~2000 LOC
|
||||
│ ├─ presentation/
|
||||
│ │ ├─ controllers/
|
||||
│ │ │ ├─ analytics.controller.ts ......... 331 lines
|
||||
│ │ │ └─ avm.controller.ts .............. 171 lines
|
||||
│ │ ├─ dto/ (15+ files)
|
||||
│ │ └─ interceptors/
|
||||
│ │ └─ cache-meta.interceptor.ts ....... 61 lines
|
||||
│ ├─ application/
|
||||
│ │ ├─ queries/ (15+ handlers)
|
||||
│ │ ├─ commands/ (3 handlers)
|
||||
│ │ └─ event-handlers/
|
||||
│ ├─ domain/
|
||||
│ │ ├─ repositories/ (2 interfaces)
|
||||
│ │ ├─ entities/ (2 entities)
|
||||
│ │ ├─ services/ (2 interfaces)
|
||||
│ │ └─ events/
|
||||
│ ├─ infrastructure/
|
||||
│ │ ├─ repositories/ (2 implementations)
|
||||
│ │ └─ services/ (6 implementations)
|
||||
│ └─ analytics.module.ts
|
||||
│
|
||||
├─ shared/ ............................. Reusable utilities
|
||||
│ ├─ infrastructure/
|
||||
│ │ ├─ cache.service.ts ............... 191 lines (core pattern)
|
||||
│ │ ├─ decorators/
|
||||
│ │ │ └─ cacheable.decorator.ts ....... 57 lines
|
||||
│ │ ├─ cache-meta.store.ts
|
||||
│ │ ├─ logger.service.ts
|
||||
│ │ ├─ prisma.service.ts
|
||||
│ │ ├─ redis.service.ts
|
||||
│ │ └─ guards/ (rate limit, auth, etc)
|
||||
│ └─ shared.module.ts
|
||||
│
|
||||
├─ auth/ ............................. Authentication
|
||||
├─ subscriptions/ ..................... Quota & billing
|
||||
├─ listings/ ......................... Listing management
|
||||
└─ [other modules]
|
||||
```
|
||||
|
||||
## 🔑 Key Imports Pattern
|
||||
|
||||
```ts
|
||||
// In analytics.controller.ts
|
||||
import { QueryBus } from '@nestjs/cqrs';
|
||||
import { JwtAuthGuard } from '@modules/auth';
|
||||
import { EndpointRateLimit, EndpointRateLimitGuard } from '@modules/shared';
|
||||
import { RequireQuota, QuotaGuard } from '@modules/subscriptions';
|
||||
|
||||
// In query handlers
|
||||
import {
|
||||
DomainException,
|
||||
CacheService,
|
||||
CachePrefix,
|
||||
CacheTTL,
|
||||
Cacheable,
|
||||
LoggerService,
|
||||
PrismaService,
|
||||
} from '@modules/shared';
|
||||
import { MARKET_INDEX_REPOSITORY, type IMarketIndexRepository } from '../../domain/...';
|
||||
|
||||
// In infrastructure repositories
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { PrismaService } from '@modules/shared';
|
||||
import { type IMarketIndexRepository } from '../../domain/...';
|
||||
```
|
||||
|
||||
## 🚀 Key Numbers
|
||||
|
||||
| Metric | Value |
|
||||
|--------|-------|
|
||||
| Controllers | 2 |
|
||||
| Endpoints | 24 |
|
||||
| Query Handlers | 15+ |
|
||||
| DTOs | 15+ |
|
||||
| Repository Interfaces | 2 |
|
||||
| Repository Implementations | 2 |
|
||||
| Services (interfaces) | 2 |
|
||||
| Services (implementations) | 6+ |
|
||||
| Cache Prefixes | 10+ |
|
||||
| Cache TTLs | 20+ |
|
||||
| Total LOC (analytics module) | ~2000 |
|
||||
| Total LOC (shared/cache) | ~250 |
|
||||
|
||||
Reference in New Issue
Block a user