MeshiTrack/docs/cross-cutting.md

226 lines
7.3 KiB
Markdown
Raw Permalink Normal View History

2026-03-27 14:50:34 +09:00
# 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**:
```typescript
// 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:
```typescript
{
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:
```typescript
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:
```typescript
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)