Files
marketplace/docs/architecture.md
delta-lynx-89e8 dbbbbd26f4 Add rental system: listings, bookings, payments, payouts, reviews
Full rental marketplace with 6 categories (apartment, house, car, motorcycle, bicycle, ebike).
Booking workflow: create → confirm → pay → active → complete → payout.
Landlord dashboard, admin moderation, availability calendar, Stripe Connect payouts.
14 QA bugs found and fixed including validator schemas, API response types, HTTP methods.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 15:33:29 -08:00

333 lines
18 KiB
Markdown

# Architecture
## High-Level Overview
```
┌──────────────────────────────────────────────────────────────┐
│ Client (React) │
│ http://localhost:5173 (Vite) │
│ │
│ ┌──────────┐ ┌──────────┐ ┌───────────┐ ┌────────────┐ │
│ │ Pages │ │Components│ │ Context │ │ API Layer │ │
│ └──────────┘ └──────────┘ └───────────┘ └─────┬──────┘ │
│ │ │
└────────────────────────────────────────────────────┼─────────┘
REST API (HTTP) │ WebSocket │
│ (Socket.io) │
┌──────────────────────────────────┼─────────────────┼─────────┐
│ Server (Express) │ │
│ http://localhost:3000 │ │
│ │ │
│ ┌───────────┐ ┌────────────┐ ┌──────────────┐ │ │
│ │ Middleware │ │ Routes │ │ Socket.io │◄─┘ │
│ │ (auth, │ │ (REST API) │ │ (real-time) │ │
│ │ upload, │ └─────┬──────┘ └──────┬───────┘ │
│ │ validate)│ │ │ │
│ └───────────┘ │ │ │
│ ┌────┴────────────────┴──────┐ │
│ │ Prisma ORM │ │
│ └────────────┬───────────────┘ │
│ │ │
└───────────────────────────────┼──────────────────────────────┘
┌───────────────────────────────┼──────────────────────────────┐
│ PostgreSQL 17 (Docker) │
│ marketplace-postgres container │
│ Volume: marketplace-pgdata │
└──────────────────────────────────────────────────────────────┘
External Services:
┌─────────┐ ┌──────────────┐
│ Stripe │ │ Google Maps │
│ (pay) │ │ (location) │
└─────────┘ └──────────────┘
```
## Client-Server Communication
### REST API
The client communicates with the server via a JSON REST API. All endpoints are prefixed with `/api/`.
| Prefix | Purpose |
|-----------------------|----------------------------------------------|
| `/api/auth` | Registration, login, token refresh, logout |
| `/api/users` | User profiles, settings, privacy |
| `/api/listings` | CRUD for buy/sell listings, search, favorites|
| `/api/offers` | Create, counter, accept, decline offers |
| `/api/chat` | Conversations and message history |
| `/api/notifications` | Fetch and mark notifications as read |
| `/api/payments` | Stripe payment intents, webhooks |
| `/api/location` | Google Maps autocomplete proxy |
| `/api/reports` | Content/user reporting |
| `/api/subscriptions` | Seller subscription management |
| `/api/promotions` | Listing promotion/boost |
| `/api/admin` | Admin dashboard, user/listing management |
| `/api/rentals` | Rental listing CRUD and search |
| `/api/bookings` | Booking lifecycle management |
| `/api/rental-payments`| Rental payment processing and webhooks |
| `/api/payouts` | Landlord payout management |
| `/api/rental-reviews` | Tenant/landlord reviews |
| `/api/health` | Health check endpoint |
### Socket.io (WebSocket)
Socket.io provides the real-time communication layer. The server creates a Socket.io instance attached to the same HTTP server as Express.
**Events flow:**
- The client connects with an auth token after login
- The server authenticates the socket connection and associates it with a user
- Real-time events are emitted for: new messages, notifications (offers, bookings, moderation), typing indicators, and online status
## Database Layer
### Prisma ORM
The database schema is defined in `server/prisma/schema.prisma`. Prisma generates a type-safe client used throughout the server routes.
**Key models:**
| Model | Purpose |
|-------------------|--------------------------------------------------|
| `User` | Accounts with roles, privacy settings, landlord status |
| `Session` | JWT refresh token sessions |
| `Listing` | Buy/sell items with category, condition, price |
| `ListingImage` | Image attachments for listings |
| `Offer` | Price negotiation between buyer and seller |
| `Conversation` | Chat threads between two users |
| `Message` | Individual chat messages |
| `Favorite` | User bookmarks on listings |
| `Notification` | In-app notifications (19 distinct types) |
| `Payment` | Payment records (8 payment types) |
| `Report` | Content/user reports for moderation |
| `Subscription` | Seller tier subscriptions |
| `PromotedListing` | Boosted listing placements |
| `ModerationLog` | Audit trail for moderation actions |
| `BlockedUser` | User blocking relationships |
| `RentalListing` | Rental items (apartments, cars, bikes, etc.) |
| `Booking` | Rental booking with status lifecycle |
| `RentalReview` | Reviews for completed rentals |
| `Payout` | Landlord payout tracking |
### Enums
The schema uses PostgreSQL enums extensively for type safety:
- **User roles:** `USER`, `MODERATOR`, `ADMIN`, `SUPER_ADMIN`
- **Listing categories:** `ELECTRONICS`, `FURNITURE`, `CLOTHING`, `HOME_GARDEN`, `SPORTS`, `BOOKS`, `GAMES`, `VEHICLES`, `OTHER`
- **Rental categories:** `APARTMENT`, `HOUSE`, `CAR`, `MOTORCYCLE`, `BICYCLE`, `EBIKE`
- **Booking status:** `PENDING` -> `CONFIRMED` -> `ACTIVE` -> `COMPLETED` (or `CANCELLED_*` / `REJECTED` / `EXPIRED`)
- **Cancellation policy:** `FLEXIBLE`, `MODERATE`, `STRICT`
## Authentication Flow
The application uses JWT-based authentication with access and refresh tokens.
```
┌────────┐ ┌────────┐
│ Client │ │ Server │
└───┬────┘ └───┬────┘
│ POST /api/auth/login │
│ { email, password } │
├──────────────────────────────────────►│
│ │ Verify password (bcryptjs)
│ │ Generate access token (short-lived)
│ │ Generate refresh token (long-lived)
│ │ Store session in DB
│ { accessToken, user } │
│ Set-Cookie: refreshToken (httpOnly) │
│◄──────────────────────────────────────┤
│ │
│ GET /api/listings │
│ Authorization: Bearer <accessToken> │
├──────────────────────────────────────►│
│ │ auth middleware verifies JWT
│ │ checkBanned middleware
│ { listings: [...] } │
│◄──────────────────────────────────────┤
│ │
│ POST /api/auth/refresh │
│ Cookie: refreshToken │
├──────────────────────────────────────►│
│ │ Validate refresh token
│ │ Issue new access token
│ { accessToken } │
│◄──────────────────────────────────────┤
```
### Middleware Chain
Requests pass through several middleware layers:
1. **`helmet`** -- Security headers
2. **`cors`** -- Restricts origins to `CLIENT_URL`
3. **`cookieParser`** -- Parses refresh token cookies
4. **`express-rate-limit`** -- Rate limits auth endpoints (20 requests / 15 min)
5. **`auth`** -- Verifies JWT access token, attaches user to request
6. **`checkBanned`** -- Blocks banned users
7. **`requireRole`** -- Role-based access control (e.g., admin-only routes)
8. **`upload`** -- Multer file upload handling
9. **`validate`** -- Zod schema validation for request bodies
## Payment Flow
### Marketplace Payments (Listings)
Stripe PaymentIntents are used for buy/sell transactions:
```
┌────────┐ ┌────────┐ ┌────────┐
│ Buyer │ │ Server │ │ Stripe │
└───┬────┘ └───┬────┘ └───┬────┘
│ Create PI │ │
├───────────────►│ POST /api/ │
│ │ payments │
│ ├───────────────►│ stripe.paymentIntents.create()
│ │ clientSecret │
│ │◄───────────────┤
│ clientSecret │ │
│◄───────────────┤ │
│ │ │
│ Confirm (Stripe.js) │
├────────────────────────────────►│
│ │ │
│ │ Webhook event │
│ │◄───────────────┤ payment_intent.succeeded
│ │ Update DB │
│ │ Notify seller │
│ │ │
```
### Rental Payments
Rental bookings follow a similar flow with additional handling for:
- **Security deposits** -- Held and refunded after completion
- **Landlord payouts** -- Via Stripe Connect (`stripeAccountId` on User model)
- **Commissions** -- Platform fee deducted from rental payments
- **Separate webhook endpoint** -- `/api/rental-payments/webhook`
### Payment Types
The system tracks eight distinct payment types:
| Type | Description |
|------------------------|-------------------------------------------|
| `LISTING_FEE` | Fee to publish a listing |
| `COMMISSION` | Platform commission on sales |
| `PROMOTION` | Listing boost/promotion fee |
| `SUBSCRIPTION` | Seller subscription payment |
| `RENTAL_BOOKING` | Tenant rental payment |
| `RENTAL_COMMISSION` | Platform fee on rentals |
| `RENTAL_DEPOSIT` | Security deposit hold |
| `RENTAL_DEPOSIT_REFUND`| Deposit refund after completion |
| `RENTAL_PAYOUT` | Payment to landlord via Connect |
## File Uploads
File uploads are handled by **Multer** with local disk storage.
- **Configuration:** `server/src/middleware/upload.ts`
- **Storage directory:** Configurable via `UPLOAD_DIR` env var (default: `./uploads`)
- **Serving:** Static files served at `/uploads/*` via `express.static`
- **Usage:** Listing images, rental listing photos, user avatars
Files are stored on the local filesystem. In production, this should be replaced with cloud storage (e.g., S3, Cloudflare R2).
## Real-Time Communication
Socket.io is configured in `server/src/socket/index.ts` and attached to the HTTP server alongside Express.
### Connection Flow
1. Client establishes WebSocket connection after successful login
2. Server authenticates the connection using the JWT token
3. User is registered as "online" and associated with their socket ID
### Event Types
| Category | Events |
|-----------------|--------------------------------------------------|
| **Chat** | New message, typing indicator, read receipts |
| **Notifications**| New offer, offer response, booking updates, payout status, moderation actions |
| **Presence** | User online/offline status |
### Architecture
```
Client A ──WebSocket──┐
├──► Socket.io Server ──► Emit to Client B
Client B ──WebSocket──┘ │
┌─────┴─────┐
│ Express │
│ routes │
│ can emit │
│ via io │
└───────────┘
```
Express routes access the Socket.io instance via `app.get('io')` to emit events from REST handlers (e.g., sending a notification when an offer is created via the REST API).
## Security
| Concern | Implementation |
|----------------------|---------------------------------------------------|
| HTTP headers | Helmet (XSS, clickjacking, MIME sniffing) |
| CORS | Restricted to `CLIENT_URL` |
| Rate limiting | 20 req/15 min on `/api/auth/login` and `/register`|
| Input validation | Zod schemas on all mutation endpoints |
| Password hashing | bcryptjs |
| Token storage | Refresh token in httpOnly cookie |
| Role-based access | `requireRole` middleware for admin/mod routes |
| User banning | `checkBanned` middleware on protected routes |
| Webhook verification | Stripe webhook signature verification |
## Deployment Considerations
### Database
- Use a managed PostgreSQL service (e.g., AWS RDS, Supabase, Neon) in production
- Run `npx prisma migrate deploy` to apply migrations
- The Docker setup is intended for local development only
### File Storage
- Replace local Multer storage with S3-compatible storage (AWS S3, Cloudflare R2, MinIO)
- Update the upload middleware and static file serving accordingly
### Environment
- Set strong, unique values for `JWT_SECRET` and `JWT_REFRESH_SECRET`
- Use production Stripe keys and configure webhook endpoints
- Set `CLIENT_URL` to the deployed frontend URL
### Scaling
- **Horizontal scaling:** Socket.io requires a Redis adapter (`@socket.io/redis-adapter`) for multi-instance deployments
- **Static assets:** Serve the built React app via a CDN or reverse proxy (Nginx, Cloudflare)
- **API:** The Express server is stateless (JWT-based auth) and can be scaled horizontally behind a load balancer
### Recommended Production Stack
```
┌───────────┐
│ CDN / │
│ Nginx │
└─────┬─────┘
┌───────────┴───────────┐
│ │
┌────────▼─────┐ ┌─────────▼────────┐
│ React Static │ │ Express API x N │
│ (built) │ │ (load balanced) │
└──────────────┘ └────────┬──────────┘
┌───────────────┼───────────────┐
│ │ │
┌──────▼──┐ ┌──────▼──┐ ┌───────▼──┐
│PostgreSQL│ │ Redis │ │ S3 / R2 │
│(managed) │ │(sockets)│ │ (files) │
└──────────┘ └─────────┘ └──────────┘
```