- Fix port numbers across all docs (API :3001, Web :3000) - Add 6 missing modules to README, CLAUDE.md, and architecture doc (agents, health, inquiries, leads, reviews, metrics/web-vitals) - Add Swagger UI reference and /api/v1 prefix notes - Create docs/api-endpoints.md with complete REST API reference - Create docs/README.md as documentation index - Update deployment guide with Loki, Promtail, pg-backup services - Update health check table with all current endpoints Co-Authored-By: Paperclip <noreply@paperclip.ing>
13 KiB
13 KiB
Architecture
System Overview
GoodGo Platform AI is a monorepo containing a NestJS backend, Next.js frontend, Python AI services, and MCP (Model Context Protocol) servers. The system is orchestrated with Turborepo and pnpm workspaces.
┌──────────────────────────────────────────┐
│ Client (Browser) │
└──────────────────┬───────────────────────┘
│
┌──────────────────▼───────────────────────┐
│ Next.js 14 (apps/web) │
│ ┌─────────┐ ┌──────────┐ ┌───────┐ │
│ │ Pages │ │Components│ │Zustand │ │
│ │(App │ │(UI + │ │(State) │ │
│ │ Router) │ │ Domain) │ │ │ │
│ └─────────┘ └──────────┘ └───────┘ │
└──────────────────┬───────────────────────┘
│ REST API
┌──────────────────▼───────────────────────┐
│ NestJS 11 (apps/api) │
│ │
│ ┌────────┐ ┌────────┐ ┌─────────────┐ │
│ │ Auth │ │Listings│ │ Payments │ │
│ ├────────┤ ├────────┤ ├─────────────┤ │
│ │ Search │ │ Admin │ │Subscriptions│ │
│ ├────────┤ ├────────┤ ├─────────────┤ │
│ │Analytics│ │ MCP │ │Notifications│ │
│ ├────────┤ ├────────┤ ├─────────────┤ │
│ │ Agents │ │Inquires│ │ Leads │ │
│ ├────────┤ ├────────┤ ├─────────────┤ │
│ │Reviews │ │ Health │ │ Metrics │ │
│ └────────┘ └────────┘ └─────────────┘ │
└───┬─────┬──────┬─────────┬──────────────┘
│ │ │ │
┌────────────▼┐ ┌─▼────┐ ▼ ┌───▼──────────┐
│ PostgreSQL │ │Redis │ │ │ Typesense │
│ + PostGIS │ │ │ │ │ (Search) │
└─────────────┘ └──────┘ │ └──────────────┘
│
┌───────────────▼──────────────────────┐
│ MCP Servers (libs/mcp-servers) │
│ ┌──────────┐┌──────────┐┌────────┐ │
│ │ Property ││ Market ││Valua- │ │
│ │ Search ││Analytics ││tion │ │
│ └──────────┘└──────────┘└───┬────┘ │
└──────────────────────────────┼───────┘
│
┌──────────────────────────────▼───────┐
│ AI Services (libs/ai-services) │
│ FastAPI + XGBoost + Claude API │
│ ┌──────────┐ ┌────────────────┐ │
│ │ AVM │ │ Moderation │ │
│ │(Pricing) │ │ (Claude API) │ │
│ └──────────┘ └────────────────┘ │
└──────────────────────────────────────┘
Module Architecture
Each API module follows a layered Domain-Driven Design structure:
modules/{module-name}/
├── presentation/ # HTTP layer
│ ├── controllers/ # Route handlers
│ └── dtos/ # Request/response DTOs (class-validator)
├── application/ # Business orchestration
│ ├── commands/ # Write operations (CQRS)
│ ├── queries/ # Read operations (CQRS)
│ ├── handlers/ # Command/query handlers
│ └── services/ # Application services
├── domain/ # Core business logic
│ ├── entities/ # Domain entities & value objects
│ ├── repositories/ # Repository interfaces (abstractions)
│ └── events/ # Domain events
├── infrastructure/ # External integrations
│ ├── repositories/ # Prisma repository implementations
│ └── services/ # External service adapters
└── {module-name}.module.ts # NestJS module definition
CQRS Pattern
The platform uses NestJS CQRS (@nestjs/cqrs) for complex operations:
- Commands — mutate state (create listing, process payment, register user)
- Queries — read state (search properties, get analytics)
- Events — cross-module communication (listing published → index in Typesense, payment completed → send notification)
Shared Module
The shared/ module provides cross-cutting concerns:
- Prisma service — database connection and client
- Redis service — caching and session management
- Guards — JWT auth guard, roles guard, throttle guard
- Pipes — global validation pipe (whitelist mode)
- Filters — exception filters for consistent error responses
- Decorators —
@CurrentUser(),@Roles(),@Public()
Data Flow
Property Listing Lifecycle
DRAFT → PENDING_REVIEW → ACTIVE → RESERVED → SOLD/RENTED
│ │
▼ │
REJECTED EXPIRED
- Seller/Agent creates listing (status:
DRAFT) - Seller submits for review →
PENDING_REVIEW - Admin moderates (AI moderation score assists) →
ACTIVEorREJECTED - Active listings are indexed in Typesense for search
- Buyer makes inquiry →
Inquiryrecord created - Transaction workflow:
OFFER_MADE → DEPOSIT_PAID → CONTRACT_SIGNING → COMPLETED
Search Flow
User Query → NestJS Search Module → Typesense
│
├─ Full-text on title/description
├─ Faceted filters (price, type, bedrooms)
└─ Geo-spatial (lat/long + radius)
Payment Flow
User → API (create payment) → Provider (VNPay/MoMo/ZaloPay)
│
[User pays on provider page]
│
Provider callback → API → Verify signature → Update Payment status
│
SUCCESS / FAILED
AI Valuation Flow
API Request → MCP Valuation Server → AI Service (FastAPI)
│
XGBoost Model
│
┌─────▼──────┐
│ Estimated │
│ Price + │
│ Confidence │
│ + Factors │
└─────────────┘
Database Schema
Core Models
User ──┬── Agent (1:1, for AGENT role users)
├── OAuthAccount (1:N, Google/Zalo)
├── RefreshToken (1:N, token families)
├── Subscription (1:N)
├── SavedSearch (1:N)
└── UsageRecord (1:N)
Property ──┬── PropertyMedia (1:N, images/videos)
├── Listing (1:N)
└── Valuation (1:N)
Listing ──┬── Inquiry (1:N)
├── Lead (1:N)
├── Transaction (1:N)
└── Agent (N:1)
Transaction ── Payment (1:N)
Plan ── Subscription (1:N)
MarketIndex (standalone — city/district/month aggregates)
Key Design Decisions
- PostGIS for spatial queries — property locations stored as
Pointgeometry - JSON columns for flexible data: amenities, nearby POIs, KYC data, subscription features
- Token family tracking for secure refresh token rotation (detects reuse attacks)
- Soft statuses over soft deletes — listings use status workflow, not
deletedAt
MCP Servers
Three Model Context Protocol servers provide AI tool interfaces:
| Server | Tools | Data Source |
|---|---|---|
| Property Search | search_properties, compare_properties, get_property_details |
Typesense |
| Market Analytics | get_market_report, analyze_trends, get_price_indices |
PostgreSQL |
| Valuation | estimate_valuation, extract_features, compare_valuations |
AI Services (FastAPI) |
MCP servers are registered via McpModule in NestJS, managed by McpRegistryService, and exposed through McpTransportController over HTTP.
AI Services
Python FastAPI microservice (libs/ai-services/) provides:
| Endpoint | Model | Purpose |
|---|---|---|
POST /avm/estimate |
XGBoost | Property price estimation with confidence score |
POST /moderation/check |
Claude API | Content moderation for listing descriptions |
GET /health |
— | Health check |
- Underthesea handles Vietnamese text tokenization and NLP preprocessing
- XGBoost model uses engineered features (area, location, property type, amenities)
Security
- JWT + Refresh Token Rotation — 15-minute access tokens, 7-day refresh tokens with family-based rotation
- OAuth — Google and Zalo social login
- Rate Limiting — 60 req/60s default, 10 req/60s for auth endpoints
- Helmet — HTTP security headers
- CORS — Configurable origins via
CORS_ORIGINS - Input Validation — class-validator (backend), Zod (frontend)
- Content Sanitization — sanitize-html for user-generated content
- KYC — Agent verification workflow (NONE → PENDING → VERIFIED/REJECTED)
Monitoring
- Prometheus scrapes
/api/v1/metricsendpoint every 15 seconds - Grafana dashboards auto-provisioned from
monitoring/grafana/dashboards/ - Loki + Promtail aggregate container logs; viewable in Grafana
- Pino structured JSON logging with configurable log levels
- Metrics include HTTP request duration, error rates, web vitals, and custom business metrics
- Log retention: 15 days (configured in
monitoring/loki/loki-config.yml)
Event System
The platform uses @nestjs/event-emitter for loose coupling between modules:
listing.published→ Typesense indexer updates search indexpayment.completed→ Notification service sends confirmationinquiry.created→ Agent notification triggereduser.registered→ Welcome email sent