# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## Project Overview Customer portal with BFF (Backend for Frontend) architecture. Users can self-register, manage subscriptions, view/pay invoices, and manage support cases. **Systems of Record:** - **WHMCS**: Billing, subscriptions, invoices, authoritative address storage - **Salesforce**: CRM (Accounts, Contacts, Cases), order address snapshots - **Portal**: Next.js UI + NestJS BFF ## Development Commands ```bash # Start development environment (PostgreSQL + Redis via Docker) pnpm dev:start # Start both frontend and backend with hot reload pnpm dev # Build domain package (required before running apps if domain changed) pnpm domain:build # Type checking pnpm type-check # Linting pnpm lint pnpm lint:fix # Database commands pnpm db:migrate # Run migrations pnpm db:studio # Open Prisma Studio GUI pnpm db:generate # Generate Prisma client # Run tests pnpm test # All packages pnpm --filter @customer-portal/bff test # BFF only # Stop services pnpm dev:stop ``` **Access points:** - Frontend: http://localhost:3000 - Backend API: http://localhost:4000/api - Prisma Studio: http://localhost:5555 ## Architecture ### Monorepo Structure ``` apps/ ├── portal/ # Next.js 15 frontend (React 19, Tailwind, shadcn/ui) └── bff/ # NestJS 11 backend (Prisma, BullMQ, Zod validation) packages/ └── domain/ # Unified domain layer (contracts, schemas, provider mappers) ``` ### Three-Layer Boundary (Non-Negotiable) | Layer | Location | Purpose | | ------ | ------------------ | ------------------------------------------------------------------------------ | | Domain | `packages/domain/` | Shared contracts, Zod validation, provider mappers. Framework-agnostic. | | BFF | `apps/bff/` | HTTP boundary, orchestration, external integrations (Salesforce/WHMCS/Freebit) | | Portal | `apps/portal/` | UI layer. Pages are thin wrappers over feature modules. | ### Domain Package Structure Each domain module follows this pattern: ``` packages/domain// ├── contract.ts # Normalized types (provider-agnostic) ├── schema.ts # Zod validation schemas ├── index.ts # Public exports └── providers/ # Provider-specific adapters (BFF-only) └── whmcs/ ├── raw.types.ts # Raw API response types └── mapper.ts # Transform raw → domain ``` ### Import Rules (ESLint Enforced) **Allowed (Portal + BFF):** ```typescript import type { Invoice } from "@customer-portal/domain/billing"; import { invoiceSchema } from "@customer-portal/domain/billing"; import { Formatting } from "@customer-portal/domain/toolkit"; ``` **Allowed (BFF only):** ```typescript import { Whmcs } from "@customer-portal/domain/billing/providers"; ``` **Forbidden everywhere:** ```typescript // Root import import { Billing } from "@customer-portal/domain"; // Deep imports beyond entrypoints import { Invoice } from "@customer-portal/domain/billing/contract"; import { transformWhmcsInvoice } from "@customer-portal/domain/billing/providers/whmcs/mapper"; ``` **Forbidden in Portal:** ```typescript // Portal must NEVER import provider adapters import { Whmcs } from "@customer-portal/domain/billing/providers"; ``` ### Portal Feature Architecture ``` apps/portal/src/ ├── app/ # Next.js App Router (thin route shells, no API calls) ├── components/ # Atomic design: atoms/, molecules/, organisms/, templates/ ├── core/ # App infrastructure: api/, logger/, providers/ ├── features/ # Feature modules with: api/, stores/, components/, hooks/, views/ └── shared/ # Cross-feature: hooks/, utils/, constants/ ``` **Feature module pattern:** - `api/`: Data fetching layer (built on shared apiClient) - `stores/`: Zustand state management - `hooks/`: React Query hooks wrapping API services - `components/`: Feature-specific UI - `views/`: Page-level view components - `index.ts`: Feature public API (barrel exports) ### BFF Integration Pattern **Map Once, Use Everywhere:** ``` External API → Integration Service → Domain Mapper → Domain Type → Use Directly ``` Integration services live in `apps/bff/src/integrations/{provider}/`: - `services/`: Connection services, entity-specific services - `utils/`: Query builders (SOQL, etc.) Domain mappers live in `packages/domain/{module}/providers/{provider}/`: - Integration services fetch data and call domain mappers - No business logic in integration layer - No double transformation ## Key Patterns ### Validation (Zod-First) - Schemas live in domain: `packages/domain//schema.ts` - Derive types from schemas: `export type X = z.infer` - Query params: use `z.coerce.*` for URL strings ### BFF Controllers - Controllers are thin: no business logic, no Zod imports - Use `createZodDto(schema)` with global `ZodValidationPipe` - Integrations: build queries in utils, fetch data, transform via domain mappers ### Logging - BFF: Use `nestjs-pino` Logger, inject via constructor - Portal: Use `@/core/logger` - No `console.log` in production code (ESLint enforced) ### Naming - No `any` in public APIs - No `console.log` (use logger) - Avoid `V2` suffix in service names ## Documentation Read before coding: - `docs/README.md` (entrypoint) - `docs/development/` (BFF/Portal/Domain patterns) - `docs/architecture/` (boundaries) - `docs/integrations/` (external API details) **Rule: Never guess endpoint behavior or payload shapes. Find docs or existing implementation first.**