5.8 KiB
5.8 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Project Overview
Xpeditis is a B2B SaaS maritime freight booking platform. Freight forwarders search and compare real-time shipping rates, book containers, and manage shipments. Built as a monorepo with NestJS backend (Hexagonal Architecture) and Next.js 14 frontend.
Development Commands
# Start infrastructure (PostgreSQL + Redis + MinIO)
docker-compose up -d
# Install dependencies
npm install && cd apps/backend && npm install && cd ../frontend && npm install
# Run database migrations
cd apps/backend && npm run migration:run
# Start development servers
npm run backend:dev # http://localhost:4000, Swagger: /api/docs
npm run frontend:dev # http://localhost:3000
Testing
# Backend (from apps/backend/)
npm test # Unit tests
npm test -- booking.entity.spec.ts # Single file
npm run test:cov # With coverage
npm run test:integration # Integration tests (needs DB/Redis)
npm run test:e2e # E2E tests
# Frontend (from apps/frontend/)
npm test
npx playwright test # E2E tests
Database Migrations
cd apps/backend
npm run migration:generate -- src/infrastructure/persistence/typeorm/migrations/MigrationName
npm run migration:run
npm run migration:revert
Build
npm run backend:build # Compiles TS with path alias resolution (tsc-alias)
npm run frontend:build # Next.js production build
Architecture
Hexagonal Architecture (Backend)
apps/backend/src/
├── domain/ # CORE - Pure TypeScript, NO framework imports
│ ├── entities/ # Booking, RateQuote, User (with private props, static create())
│ ├── value-objects/# Money, Email, BookingNumber (immutable, validated)
│ ├── services/ # Domain services
│ ├── ports/
│ │ ├── in/ # Use case interfaces
│ │ └── out/ # Repository interfaces (SPIs)
│ └── exceptions/ # Domain exceptions
├── application/ # Controllers, DTOs, Guards (depends ONLY on domain)
└── infrastructure/ # TypeORM, Redis, Carrier APIs (depends ONLY on domain)
Critical Rules:
- Domain layer: Zero imports from NestJS, TypeORM, Redis
- Dependencies flow inward: Infrastructure → Application → Domain
- Use path aliases:
@domain/*,@application/*,@infrastructure/* - Domain tests run without NestJS TestingModule
Frontend (Next.js 14 App Router)
apps/frontend/
├── app/ # App Router pages
│ ├── dashboard/ # Protected routes
│ │ ├── bookings/ # Booking management
│ │ ├── admin/ # Admin features (ADMIN role)
│ │ └── settings/ # User/org settings
│ └── carrier/ # Carrier portal (magic link auth)
└── src/
├── components/ # React components (shadcn/ui in ui/)
├── hooks/ # useBookings, useNotifications
├── lib/api/ # API client modules
└── types/ # TypeScript definitions
Path aliases: @/* → ./src/*, @/components/*, @/lib/*, @/hooks/*
Key Patterns
Entity Pattern (Domain)
export class Booking {
private readonly props: BookingProps;
static create(props: Omit<BookingProps, 'bookingNumber'>): Booking { ... }
updateStatus(newStatus: BookingStatus): Booking { // Returns new instance
return new Booking({ ...this.props, status: newStatus });
}
}
Repository Pattern
- Interface in
domain/ports/out/booking.repository.ts - Implementation in
infrastructure/persistence/typeorm/repositories/typeorm-booking.repository.ts - Separate mappers for Domain ↔ ORM entity conversions
Circuit Breaker (External APIs)
- Library:
opossum, Timeout: 5s - Used for carrier API calls (Maersk, MSC, CMA CGM)
Caching
- Redis with 15-min TTL for rate quotes
- Cache key format:
rate:{origin}:{destination}:{containerType}
Business Rules
- Booking number format:
WCM-YYYY-XXXXXX - Rate quotes expire after 15 minutes
- Multi-currency: USD, EUR
- RBAC Roles: ADMIN, MANAGER, USER, VIEWER, CARRIER
Carrier Portal Workflow
- Admin creates CSV booking → assigns carrier
- Email with magic link sent (1-hour expiry)
- Carrier auto-login → accept/reject booking
- Activity logged in
carrier_activitiestable
Tech Stack
Backend: NestJS 10, TypeORM 0.3, PostgreSQL 15, Redis 7, Argon2, Pino, Sentry Frontend: Next.js 14, React 18, TanStack Query/Table, React Hook Form + Zod, Tailwind + shadcn/ui, Socket.IO
Common Pitfalls
- Never import NestJS/TypeORM in domain layer
- Never use
anytype (strict mode enabled) - Never use
DATABASE_SYNC=truein production - Never modify applied migrations - create new ones
- Always validate DTOs with
class-validator - Always create mappers for Domain ↔ ORM conversions
Adding a New Feature
- Domain Entity →
domain/entities/*.entity.ts(pure TS, unit tests) - Value Objects →
domain/value-objects/*.vo.ts(immutable) - Port Interface →
domain/ports/out/*.repository.ts - ORM Entity →
infrastructure/persistence/typeorm/entities/*.orm-entity.ts - Generate Migration →
npm run migration:generate -- ... - Repository Impl →
infrastructure/persistence/typeorm/repositories/ - DTOs →
application/dto/(with class-validator decorators) - Controller →
application/controllers/(with Swagger decorators) - Module → Register and import in
app.module.ts
Documentation
- API Docs: http://localhost:4000/api/docs (Swagger)
- Architecture:
docs/architecture.md - Carrier Portal API:
apps/backend/docs/CARRIER_PORTAL_API.md - Full docs index:
docs/README.md