Files
goodgo-platform/docs/audits/AUDIT_TECHNICAL_REFERENCE.md
Ho Ngoc Hai b93c28fa01 chore: organize docs — move 37 files from root into docs/ subfolders
Root now contains only essential files:
  README.md, CLAUDE.md, CHANGELOG.md, CONTRIBUTING.md

Reorganized into:
  docs/audits/       — all audit reports & checklists (71 files)
  docs/architecture/  — codebase overview, implementation plan
  docs/guides/        — auth guide, implementation checklist
  docs/load-testing/  — k6 load test guides & endpoints
  docs/security/      — payment & security reviews

Also removed 5 untracked debug/investigation files and
cleaned up playwright-report/ & test-results/ artifacts.

Co-Authored-By: Claude Opus 4 (1M context) <noreply@anthropic.com>
2026-04-13 12:09:14 +07:00

16 KiB

GoodGo Platform AI - Technical Reference & Deep Dive

For Developers & Architects


BACKEND MODULE HIERARCHY

Core Module Dependencies

SharedModule (lowest level)
    ├── Infrastructure Services
    ├── Middleware & Guards
    ├── Decorators & Utilities
    └── Domain Enums & Types
        ↓
    ├→ AuthModule
    ├→ HealthModule
    └→ All Feature Modules
        ├→ AdminModule (audit, user management)
        ├→ AgentsModule (agent profiles, specialized deals)
        ├→ AnalyticsModule (market reports, valuation history)
        ├→ InquiriesModule (property inquiries)
        ├→ LeadsModule (agent leads management)
        ├→ ListingsModule (property listings)
        ├→ NotificationsModule (FCM push, email)
        ├→ PaymentsModule (VNPay integration)
        ├→ ReviewsModule (property reviews)
        ├→ SearchModule (Typesense full-text search)
        ├→ SubscriptionsModule (billing, usage metering)
        └→ MetricsModule (Prometheus metrics)

DOMAIN MODELS - RELATIONSHIPS

User Role Hierarchy

User (root entity)
├── Role: BUYER → Can browse, search, inquire, purchase
├── Role: SELLER → Can create listings, receive inquiries, sell
├── Role: AGENT → Extends Seller + lead management
└── Role: ADMIN → All permissions + moderation

Listing Workflow

User (SELLER)
    ↓ creates
Property + PropertyMedia
    ↓ associated with
Listing (status: DRAFT → PUBLISHED → SOLD → ARCHIVED)
    ↓ receives
Inquiry (from BUYER/AGENT)
    ↓ converts to
Transaction (buyer-seller exchange)
    ↓ followed by
Review + UsageRecord (analytics)

Payment Flow

User (Subscription Start)
    ↓
Plan (monthly/yearly pricing)
    ↓
Subscription (active/cancelled/expired)
    ↓
Payment (processed via VNPay)
    ├── Idempotency Key (prevents duplicates)
    └── Status Tracking
            ↓
        UsageRecord (track consumed resources)

AUTHENTICATION FLOW

JWT Token Lifecycle

1. User Login (email + password OR OAuth)
   └→ Verify credentials (bcrypt hash)
   
2. Generate Tokens
   ├→ AccessToken (15 min, bearer auth)
   └→ RefreshToken (7 days, stored in DB)
       └→ Token Family (refresh rotation)
   
3. Return to Client
   └→ Set Secure HTTP-Only Cookie (refresh token)
   
4. API Access
   ├→ Authorization: Bearer <accessToken>
   ├→ Guard validates JWT signature
   └→ Inject user context into request
   
5. Token Refresh
   ├→ Client sends refresh token
   ├→ Verify token family (revocation check)
   ├→ Rotate token (issue new family)
   └→ Return new access token

DATABASE SCHEMA - KEY INDEXES

Query Optimization Strategy

User Table:
├── idx_user_role (BUYER/SELLER/AGENT/ADMIN filtering)
├── idx_user_kyc_status (compliance checks)
├── idx_user_active (active user queries)
├── idx_user_deleted_at (soft delete filtering)
└── idx_role_active_created (complex queries: role + active + order by)

Listing Table:
├── idx_listing_status (published, archived, sold filtering)
├── idx_listing_user_created (user's listings ordered)
└── idx_listing_location_geo (PostGIS spatial queries)

Payment Table:
├── idx_payment_user_status (user's payment history)
├── idx_payment_idempotency (duplicate prevention)
└── idx_payment_external_ref (payment gateway reconciliation)

Search Optimization:
└── Typesense (full-text + geo-search, delegated from DB)

SECURITY LAYERS - DETAILED

Layer 1: Network Level

HTTP Request
    ↓
Helmet (Express middleware)
├── Content-Security-Policy
│   └── Blocks inline scripts, restricts origins
├── X-Frame-Options: DENY
│   └── Prevents clickjacking
├── Strict-Transport-Security (HSTS)
│   └── Forces HTTPS for 31536000 seconds
├── X-Content-Type-Options: nosniff
│   └── Prevents MIME-sniffing
└── Referrer-Policy: strict-origin-when-cross-origin
    └── Controls referrer leaks

Layer 2: Application Level

Request Processing
    ↓
1. CORS Validation
   └── Whitelist check (process.env.CORS_ORIGINS)
   
2. CSRF Protection
   ├── Read (GET): Set __Host-X-CSRF-Token cookie
   └── Write (POST/PUT/PATCH/DELETE):
       ├── Verify X-CSRF-Token header
       └── Validate cookie matches header (double-submit)
   
3. Input Sanitization
   ├── Remove XSS vectors (sanitize-html)
   ├── Whitelist validation (class-validator)
   └── Type coercion (class-transformer)
   
4. Rate Limiting
   ├── Global: 60 req/min per IP
   ├── Auth: 10 req/min per IP (login brute-force protection)
   └── Payments: 20 req/min per IP (webhook replay protection)

Layer 3: Data Level

Field Encryption (PII Protection)
├── FieldEncryptionService
│   ├── AES-256-GCM encryption
│   ├── Field-level (can query by hash)
│   └── Key derivation from master secret
├── Email: Encrypted + hashed (both in DB)
├── Phone: Encrypted + hashed (both in DB)
└── KYC Data: Encrypted JSON storage

Audit Trail
├── AdminAuditLog captures:
│   ├── User ID (who)
│   ├── Action (what)
│   ├── Target entity (where)
│   ├── Changes (before/after)
│   └── Timestamp (when)
└── Queryable for compliance

Layer 4: Authorization

Route Handler
    ↓
@UseGuards(JwtGuard, RoleGuard)
    ├── Extract JWT from Authorization header
    ├── Validate signature (HS256)
    ├── Check token expiration
    ├── Inject user context (request.user)
    └── Verify role (BUYER/SELLER/AGENT/ADMIN)
        └── Reject if insufficient permissions

CQRS PATTERN IMPLEMENTATION

Command Pattern (State Changes)

CreateListingCommand
├── Input: CreateListingDTO
├── Handler: CreateListingCommandHandler
│   ├── Validate inputs
│   ├── Check user permissions
│   ├── Create Property entity
│   ├── Create Listing entity
│   ├── Emit ListingCreatedEvent
│   └── Update search index
└── Output: CreatedListingDTO

Flow:
Controller → Command → CommandHandler → Domain → Event → Repository → Cache invalidate

Query Pattern (Read-only)

GetListingQuery
├── Input: ListingId
├── Handler: GetListingQueryHandler
│   ├── Check cache (Redis)
│   ├── If hit: return cached
│   └── If miss:
│       ├── Query database
│       ├── Cache result (TTL-based)
│       └── Return to client
└── Output: ListingDTO

Flow:
Controller → Query → QueryHandler → Repository → Cache store → Response

CACHING STRATEGY

Multi-Level Caching

Level 1: Browser Cache
├── Static assets (CSS, JS)
├── Max-Age: 31536000 (1 year)
└── Immutable: true

Level 2: CDN Cache (if deployed)
├── JSON responses
├── Max-Age: 300 (5 min)
└── Surrogate-Key invalidation

Level 3: Application Cache (Redis)
├── User objects (TTL: 1 hour)
├── Listing details (TTL: 30 min)
├── Search results (TTL: 5 min)
└── Rate limit counters (TTL: per window)

Cache Invalidation Triggers:
├── Event-based: ListingUpdatedEvent → invalidate key
├── Time-based: TTL expiration
├── Manual: Cache.delete(key) on batch operations
└── Circuit breaker: If Redis down, bypass to DB

ERROR HANDLING & OBSERVABILITY

Exception Hierarchy

GlobalExceptionFilter (catches all)
│
├→ HttpException (known errors)
│  ├── BadRequestException (400)
│  ├── UnauthorizedException (401)
│  ├── ForbiddenException (403)
│  ├── NotFoundException (404)
│  ├── ConflictException (409)
│  └── InternalServerErrorException (500)
│
└→ Unknown Error
   └→ Sentry.captureException(error)
      ├── Capture stack trace
      ├── Attach request context
      ├── Tag by module/operation
      └── Alert ops team (if severity > WARN)

Structured Logging (Pino)
├── JSON format for log aggregation
├── Context injection (request ID, user ID)
├── Log levels: trace, debug, info, warn, error, fatal
└── Destination: stdout (collected by Loki/Promtail)

Monitoring Points

Metrics (Prometheus)
├── HTTP request latency
├── Database query time
├── Cache hit/miss ratio
├── Error rate by endpoint
├── Queue depth (background jobs)
└── Payment processing success rate

Logs (Loki)
├── Searchable by timestamp, level, service, user
├── Retention: 30 days
└── Queries: error trends, user activity, audit trail

Traces (Sentry)
├── Request waterfall
├── Database call chains
└── Error context snapshot

BACKGROUND JOBS & EVENTS

Event System

Domain Event
├── ListingCreatedEvent
├── PaymentProcessedEvent
├── NotificationScheduledEvent
└── UserDeletedEvent
    ↓
EventEmitter.emit()
    ↓
Event Subscribers (consume in order)
├── ListingCreatedEventSubscriber
│   └→ Index in Typesense
├── PaymentProcessedEventSubscriber
│   └→ Send email receipt
├── NotificationScheduledEventSubscriber
│   └→ Queue FCM push
└── UserDeletedEventSubscriber
    └→ Archive data + audit trail

Error Handling:
├── Retry policy (3 retries, exponential backoff)
├── Dead letter queue (failed events)
└── Monitoring alert (critical events failed)

FRONTEND STATE MANAGEMENT

Zustand Store Pattern

// auth-store.ts
const useAuthStore = create((set) => ({
  user: null,
  tokens: { accessToken: null, refreshToken: null },
  
  actions: {
    setUser: (user) => set({ user }),
    setTokens: (tokens) => set({ tokens }),
    logout: () => set({ user: null, tokens: null }),
  }
}))

// Component Usage
const { user, setUser } = useAuthStore()

// Persistence (automatic)
├── localStorage (client-side)
├── Hydration on page load
└── Sync across tabs (storage event)

React Query Integration

// Hook Pattern
const useListings = (filters) => {
  return useQuery({
    queryKey: ['listings', filters],
    queryFn: () => listingsApi.search(filters),
    staleTime: 5 * 60 * 1000, // 5 min
    gcTime: 10 * 60 * 1000, // 10 min (old: cacheTime)
    retry: 3,
    retryDelay: exponentialBackoff,
  })
}

// Features
├── Automatic caching by queryKey
├── Background refetching
├── Optimistic updates
├── Pagination support
└── Dependency tracking

DEPLOYMENT ARCHITECTURE

Local Development

docker-compose.yml
├── PostgreSQL (5432)
├── Redis (6379)
├── Typesense (8108)
├── MinIO (9000)
└── PgBouncer (6432 - optional)

API Server: http://localhost:3001/api/v1
Web Server: http://localhost:3000
Swagger Docs: http://localhost:3001/api/v1/docs

Production Deployment

Kubernetes Cluster
├── API Pod (NestJS)
│   ├── Port: 3001
│   ├── Resources: 2 CPU, 2GB RAM
│   ├── Replicas: 3+ (autoscaling)
│   ├── Probes: liveness + readiness
│   └── Limits: enforce resource quotas
├── Web Pod (Next.js)
│   ├── Port: 3000
│   ├── Replicas: 2+
│   └── CDN: CloudFront/Cloudflare
├── PostgreSQL (managed RDS or Kubernetes StatefulSet)
├── Redis (managed ElastiCache or Kubernetes)
└── Typesense (managed or self-hosted cluster)

Ingress → Load Balancer → Service → Pods

CI/CD PIPELINE

Automated Stages

1. Code Push to master/PR
   └→ GitHub Actions triggered
   
2. Lint Stage (2 min)
   ├── ESLint check
   └── Prettier validation
   
3. Type Check Stage (3 min)
   └── TypeScript compilation (no emit)
   
4. Unit Test Stage (5 min)
   ├── Backend: Vitest (pnpm test)
   └── Frontend: Vitest + RTL
   
5. Integration Test Stage (8 min)
   ├── Test database setup
   └── Vitest integration config
   
6. Build Stage (10 min)
   ├── NestJS build (tsc + webpack)
   ├── Next.js build (.next folder)
   └── Artifact storage
   
7. E2E Test Stage (15 min) - if CI passes
   ├── Service startup (Postgres, Redis, Typesense)
   ├── Database migration
   ├── Seed data
   ├── Playwright tests (Chromium)
   └── Report generation
   
8. Deploy Stage (5 min) - if all pass
   ├── Docker image build
   ├── Registry push
   └── Kubernetes rollout
   
Total: ~50 min (sequential) or ~15 min (parallel)

PERFORMANCE TUNING CHECKLIST

Database

  • Query analysis (EXPLAIN ANALYZE)
  • Missing indexes (pg_stat_statements)
  • Connection pooling tuned (PgBouncer)
  • Replication lag monitored
  • Backup tested (recovery time < 1 hour)

Application

  • Memory usage profiled (Node.js heap)
  • CPU throttling identified
  • Garbage collection tuned (heap snapshots)
  • Logging overhead measured
  • Dependency versions updated

Frontend

  • Bundle size analyzed (webpack analyzer)
  • Code splitting implemented (routes)
  • Images optimized (Next.js Image)
  • Critical CSS inlined
  • Web vitals tracked (LCP, FID, CLS)

Infrastructure

  • Load balancer health checks tuned
  • Autoscaling policies tested
  • Cache hit rates > 80%
  • Network latency acceptable (< 100ms)
  • Monitoring alert thresholds realistic

TROUBLESHOOTING GUIDE

"Database Connection Timeout"

Diagnosis:
1. Check if PostgreSQL container is running: docker-compose ps
2. Verify DATABASE_URL in .env
3. Check PgBouncer if production: psql -h localhost -p 6432 -U pgbouncer
4. Look for connection limit reached: SELECT count(*) FROM pg_stat_activity

Fix:
├── Restart: docker-compose restart postgres
├── Increase PgBouncer pool: PGBOUNCER_POOL_SIZE=30
└── Check slow queries: pg_stat_statements

"Redis Connection Refused"

Diagnosis:
1. Check Redis container: docker-compose ps redis
2. Verify REDIS_URL in .env
3. Check port: redis-cli -p 6379 ping
4. Check memory: redis-cli INFO memory

Fix:
├── Restart: docker-compose restart redis
├── Flush if needed: redis-cli FLUSHALL (dev only!)
└── Monitor: redis-cli --stat

"Typesense Index Not Found"

Diagnosis:
1. Check Typesense container: docker-compose ps typesense
2. Verify TYPESENSE_API_KEY in .env
3. List indexes: curl http://localhost:8108/collections -H "X-TYPESENSE-API-KEY: <key>"
4. Check sync job logs

Fix:
├── Re-seed: pnpm db:seed
├── Reindex: DELETE /listings index, then rebuild
└── Monitor: Typesense dashboard http://localhost:8108/dashboard

"Tests Failing with 'Port Already in Use'"

Diagnosis:
1. Check running processes: lsof -i :3001 (macOS) or netstat -ano (Windows)
2. Docker containers: docker ps

Fix:
├── Kill process: kill -9 <PID>
├── Stop containers: docker-compose down
├── Update port in .env.test
└── Ensure cleanup in global-teardown.ts

SECURITY CHECKLIST - PRE-DEPLOYMENT

  • JWT secrets rotated and unique
  • CORS_ORIGINS finalized (no localhost in prod)
  • Database credentials strong (> 16 chars, random)
  • MinIO/AWS S3 credentials secure (IAM policy restricted)
  • OAuth client secrets masked
  • SSL certificate installed (HTTPS)
  • HSTS preload submitted
  • Security headers tested (securityheaders.com)
  • OWASP Top 10 reviewed
  • Penetration test scheduled
  • Rate limits tuned (no bypass possible)
  • Audit logging verified
  • Backup encryption enabled
  • Incident response plan documented
  • On-call rotation configured