MeshiTrack/docs/cross-cutting.md

7.3 KiB

Cross-Cutting Concerns

Aspects that span all phases and must be maintained consistently throughout development.


API Versioning

  • All endpoints prefixed with /api/v1/
  • Version is part of the URL, not headers
  • When breaking changes are needed, introduce /api/v2/ alongside v1
  • Deprecation: v1 endpoints log warnings 3 months before removal

Pagination

All list endpoints use cursor-based pagination:

// Request
GET /api/v1/products?cursor=abc123&limit=20

// Response
{
  "data": [...],
  "pagination": {
    "cursor": "def456",      // Pass as next cursor, null = no more pages
    "hasMore": true,
    "total": 150              // Total count (optional, can be expensive)
  }
}
  • Default limit: 20, max: 100
  • Cursor is an opaque string (encoded _id or composite sort key)
  • Prefer cursor over offset/skip for MongoDB performance

Audit Trail

Every domain document includes:

{
  createdAt: Date; // Set on creation, never modified
  updatedAt: Date; // Updated on every modification
  createdBy: string; // userId who created
}

For sensitive operations (deletes, status transitions, admin actions), maintain an AuditLog collection:

interface AuditLog {
  id: string;
  householdId: string;
  userId: string;
  action: string; // 'product.delete', 'pantry.transition', etc.
  entityType: string; // 'Product', 'PantryItem', etc.
  entityId: string;
  changes?: Record<string, { from: any; to: any }>;
  timestamp: Date;
}

Implemented as a NestJS interceptor that logs after successful mutations.


Error Handling

Consistent error response format across all endpoints:

interface ApiError {
  statusCode: number;
  error: string; // HTTP status text
  message: string; // Human-readable message
  details?: any; // Validation errors, etc.
  timestamp: string;
  path: string;
}

Global exception filter in NestJS catches:

  • ValidationException → 400
  • UnauthorizedException → 401
  • ForbiddenException → 403
  • NotFoundException → 404
  • ConflictException → 409 (e.g., duplicate barcode)
  • TooManyRequestsException → 429 (LLM rate limit)
  • Unhandled errors → 500 (log stack trace, return generic message)

Real-Time (WebSocket)

NestJS @WebSocketGateway with Socket.IO:

  • Authentication: validate JWT on connection
  • Rooms: one room per householdId — all household members receive household events
  • Namespaces: optional per-feature namespaces (/pantry, /shopping)

Events introduced per phase:

Phase Events
3 pantry:item-added, pantry:item-updated, pantry:freshness-alert
5 shopping:item-checked, shopping:item-added, shopping:item-removed, shopping:list-updated
4 meal-plan:updated

Frontend pattern:

  • Connect on app mount, join household room
  • Use React context/Zustand store to distribute events to components
  • Optimistic UI updates with server reconciliation

Testing Strategy

Unit Tests (per module, per phase)

  • Services: business logic, calculations (nutrition calculator, freshness calculator, suggestion scoring)
  • Guards/Interceptors: auth, household scoping
  • Mocking: MongoDB operations mocked for service tests
  • Framework: Jest

Integration Tests (per module)

  • In-memory MongoDB (mongodb-memory-server) or test containers
  • Test full request → service → database → response cycle
  • Framework: Supertest + Jest

E2E Tests (per phase milestone)

  • Full Docker Compose stack
  • Automated script that exercises the happy path:
    1. Register/login user
    2. Create household
    3. Add products
    4. Create recipe
    5. Stock pantry
    6. Generate meal plan
    7. Create shopping list
    8. Check off items → add to pantry
  • Framework: Supertest or Playwright (for web UI)

Frontend Tests

  • Component tests: React Testing Library
  • Hook tests: @testing-library/react-hooks
  • E2E: Playwright for critical flows (login, add product, create recipe)

Test Coverage Targets

Type Target
Unit 80%+
Integration Key flows covered
E2E Happy path per phase

Mobile Readiness

Design decisions to facilitate React Native development later:

  1. All business logic in API: the web frontend is a thin UI layer. Mobile will consume the same API.
  2. Shared types package: packages/shared is platform-agnostic TypeScript. Mobile imports it directly.
  3. No SSR dependencies in shared code: avoid Next.js-specific imports in packages/shared.
  4. API client layer: packages/web/src/services/api.ts wraps fetch/axios with auth. Mobile will have its own but same pattern.
  5. WebSocket events: same events work on mobile (Socket.IO has React Native support).
  6. Auth: Keycloak has React Native OIDC libraries (react-native-app-auth).
  7. Image handling: API accepts standard multipart uploads — works from any client.
  8. Push notifications: Phase 3 starts with in-app notifications. Mobile phase adds Firebase Cloud Messaging (FCM) as a notification channel.

When the mobile phase begins:

  • Add packages/mobile (React Native via Expo or bare workflow)
  • Share from packages/shared
  • Build mobile-optimized UI for key flows: pantry check, shopping list, quick add

Security

  • Authentication: All API routes require valid Keycloak JWT (except health check)
  • Authorization: Household-scoped — users can only access data for their households
  • Input validation: Zod schemas validate all inputs; NestJS validation pipe rejects invalid requests
  • Rate limiting: per-IP and per-household (configurable)
  • CORS: restricted to known origins
  • Helmet: HTTP security headers via @nestjs/helmet
  • Secrets: environment variables, never committed; .env.example documents required vars
  • Image uploads: validate file type and size; store in local volume or S3-compatible storage
  • MongoDB: authenticated access, least-privilege user for the app

Performance

  • MongoDB indexes: reviewed and optimized per phase (documented in each phase doc)
  • Query profiling: enable MongoDB slow query log in dev; review before phase sign-off
  • Caching: consider Redis for:
    • Product search results (short TTL)
    • Freshness rule lookups (rarely change)
    • LLM response caching (identical inputs)
  • Denormalization: nutrition on recipes, product names on pantry items — reduces lookups
  • Pagination: cursor-based, no skip() for large collections
  • Compression: gzip responses via NestJS middleware

Observability (Future)

Not in initial phases, but plan for:

  • Structured logging: Pino or Winston with JSON format
  • Health checks: /api/v1/health returns status of MongoDB, Keycloak connectivity
  • Metrics: Prometheus-compatible endpoint (NestJS has plugins)
  • Tracing: OpenTelemetry for request tracing across services
  • Error tracking: Sentry integration (optional, self-hosted instance possible)