158 lines
5.2 KiB
Markdown
158 lines
5.2 KiB
Markdown
# Validation Cleanup Complete ✅
|
|
|
|
## Summary
|
|
|
|
Successfully removed redundant validation layer and established proper validation architecture using Zod schemas.
|
|
|
|
## Changes Made
|
|
|
|
### 1. Added URL Validation to Domain ✅
|
|
|
|
**File**: `packages/domain/common/validation.ts`
|
|
|
|
Added URL validation utilities:
|
|
- `urlSchema` - Zod schema for URL validation
|
|
- `validateUrlOrThrow()` - Throwing variant
|
|
- `validateUrl()` - Non-throwing variant returning validation result
|
|
- `isValidUrl()` - Boolean check
|
|
|
|
### 2. Refactored Invoice Services ✅
|
|
|
|
**Files Changed**:
|
|
- `apps/bff/src/modules/invoices/services/invoice-retrieval.service.ts`
|
|
- `apps/bff/src/modules/invoices/services/invoices-orchestrator.service.ts`
|
|
- `apps/bff/src/modules/invoices/invoices.controller.ts`
|
|
|
|
**Changes**:
|
|
- Removed dependency on `InvoiceValidatorService`
|
|
- Use Zod schemas directly for validation
|
|
- Controller uses `ZodValidationPipe` with `invoiceListQuerySchema`
|
|
- Services validate using domain schemas: `invoiceSchema`, `invoiceListQuerySchema`
|
|
- Removed redundant manual validation checks
|
|
|
|
### 3. Deleted Redundant Files ✅
|
|
|
|
**Deleted**:
|
|
- ❌ `apps/bff/src/modules/invoices/validators/invoice-validator.service.ts`
|
|
- ❌ `apps/bff/src/modules/invoices/types/invoice-service.types.ts`
|
|
|
|
**Created**:
|
|
- ✅ `apps/bff/src/modules/invoices/types/invoice-monitoring.types.ts` (for infrastructure types)
|
|
|
|
**Updated**:
|
|
- `apps/bff/src/modules/invoices/invoices.module.ts` - Removed validator from providers
|
|
- `apps/bff/src/modules/invoices/index.ts` - Updated exports
|
|
|
|
### 4. Preserved Infrastructure Types ✅
|
|
|
|
Created `invoice-monitoring.types.ts` for BFF-specific infrastructure concerns:
|
|
- `InvoiceServiceStats` - Monitoring/metrics
|
|
- `InvoiceHealthStatus` - Health check results
|
|
|
|
## Validation Architecture (Confirmed)
|
|
|
|
### ✅ Schema Validation (Domain - Zod)
|
|
**Location**: `packages/domain/*/schema.ts`
|
|
**Purpose**: Format, type, range validation
|
|
**Examples**:
|
|
```typescript
|
|
export const invoiceListQuerySchema = z.object({
|
|
page: z.coerce.number().int().positive().optional(),
|
|
limit: z.coerce.number().int().positive().max(100).optional(),
|
|
status: invoiceListStatusSchema.optional(),
|
|
});
|
|
```
|
|
|
|
### ✅ Business Validation (Domain - Pure Functions)
|
|
**Location**: `packages/domain/*/validation.ts`
|
|
**Purpose**: Cross-field rules, business constraints
|
|
**Examples**:
|
|
```typescript
|
|
// packages/domain/mappings/validation.ts
|
|
export function validateNoConflicts(
|
|
request: CreateMappingRequest,
|
|
existingMappings: UserIdMapping[]
|
|
): MappingValidationResult {
|
|
// Business rule: no duplicate userId or whmcsClientId
|
|
}
|
|
```
|
|
|
|
### ✅ Infrastructure Validation (BFF - Services)
|
|
**Location**: `apps/bff/src/modules/*/services/*.service.ts`
|
|
**Purpose**: Data-dependent validation (DB/API calls)
|
|
**Examples**:
|
|
```typescript
|
|
// Invoice retrieval service
|
|
private async getUserMapping(userId: string): Promise<UserMappingInfo> {
|
|
validateUuidV4OrThrow(userId); // Domain validation
|
|
const mapping = await this.mappingsService.findByUserId(userId); // DB call
|
|
if (!mapping?.whmcsClientId) {
|
|
throw new NotFoundException("WHMCS client mapping not found");
|
|
}
|
|
return mapping;
|
|
}
|
|
```
|
|
|
|
## Key Principle Established
|
|
|
|
### ❌ DON'T: Create validator services for field validation
|
|
```typescript
|
|
class XyzValidatorService {
|
|
validateField(value) {
|
|
if (!value || value < 1) throw new Error(...); // Redundant with schema
|
|
}
|
|
}
|
|
```
|
|
|
|
### ✅ DO: Use Zod schemas + validation pipe
|
|
```typescript
|
|
const xyzRequestSchema = z.object({
|
|
field: z.number().int().positive(), // Schema handles validation
|
|
});
|
|
|
|
@Post()
|
|
async create(
|
|
@Body(new ZodValidationPipe(xyzRequestSchema)) body: XyzRequest
|
|
) {
|
|
// Already validated!
|
|
}
|
|
```
|
|
|
|
## Validation Coverage by Module
|
|
|
|
| Module | Schema Validation | Business Validation | Infrastructure | Status |
|
|
|--------|------------------|-------------------|----------------|--------|
|
|
| Mappings | ✅ `domain/mappings/schema.ts` | ✅ `domain/mappings/validation.ts` | ✅ BFF service | ✅ Complete |
|
|
| Invoices | ✅ `domain/billing/schema.ts` | N/A (no business rules) | ✅ BFF service | ✅ Complete |
|
|
| Orders | ✅ `domain/orders/schema.ts` | ✅ `domain/orders/validation.ts` | ✅ BFF service | ✅ Complete |
|
|
| SIM | ✅ `domain/sim/schema.ts` | ✅ `domain/sim/validation.ts` | ✅ BFF service | ✅ Complete |
|
|
| Common | ✅ `domain/common/validation.ts` | N/A | N/A | ✅ Complete |
|
|
|
|
## Verification
|
|
|
|
✅ Domain package compiles without errors
|
|
✅ BFF compiles without errors
|
|
✅ No linter errors
|
|
✅ All validation handled by schemas at entry points
|
|
✅ Infrastructure concerns properly separated
|
|
|
|
## Benefits Achieved
|
|
|
|
1. **Single Source of Truth**: Zod schemas in domain define all field validation
|
|
2. **No Duplication**: Removed redundant validation logic
|
|
3. **Type Safety**: Schemas generate TypeScript types
|
|
4. **Consistency**: Same validation rules everywhere
|
|
5. **Maintainability**: Less code, clearer responsibilities
|
|
6. **Proper Separation**: Schema → Business → Infrastructure layers clearly defined
|
|
|
|
## Pattern for Future Development
|
|
|
|
When adding new validation:
|
|
|
|
1. **Field validation?** → Add to domain schema (Zod)
|
|
2. **Business rule?** → Add pure function to `domain/*/validation.ts`
|
|
3. **Needs DB/API?** → Keep in BFF service layer
|
|
|
|
**Never create a validator service just to duplicate what schemas already do!**
|
|
|