129 lines
4.0 KiB
Markdown
129 lines
4.0 KiB
Markdown
# Order Services Architecture Recommendation
|
|
|
|
## Recommended Structure: Enhanced Separation of Concerns
|
|
|
|
### 1. **Controller Layer** (`orders.controller.ts`)
|
|
**Responsibility**: API contract and basic validation
|
|
- DTO validation (format, types, required fields)
|
|
- Authentication/authorization
|
|
- HTTP response handling
|
|
- Minimal business logic
|
|
|
|
### 2. **Orchestrator Layer** (`order-orchestrator.service.ts`)
|
|
**Responsibility**: Workflow coordination and transaction management
|
|
- Coordinates the order creation flow
|
|
- Manages transaction boundaries
|
|
- Handles high-level error scenarios
|
|
- Calls other services in correct sequence
|
|
|
|
### 3. **Validator Layer** (`order-validator.service.ts`)
|
|
**Responsibility**: ALL validation logic (business + technical)
|
|
```typescript
|
|
class OrderValidator {
|
|
// API-level validation (move from DTO)
|
|
validateRequestFormat(body: any): CreateOrderBody
|
|
|
|
// Business validation (current)
|
|
validateUserMapping(userId: string): Promise<UserMapping>
|
|
validatePaymentMethod(userId: string, clientId: number): Promise<void>
|
|
validateSKUs(skus: string[], pricebookId: string): Promise<void>
|
|
validateBusinessRules(orderType: string, skus: string[]): void
|
|
validateInternetDuplication(userId: string, clientId: number): Promise<void>
|
|
|
|
// Complete validation (orchestrates all checks)
|
|
async validateCompleteOrder(userId: string, body: any): Promise<{
|
|
validatedBody: CreateOrderBody,
|
|
userMapping: UserMapping
|
|
}>
|
|
}
|
|
```
|
|
|
|
### 4. **Builder Layer** (`order-builder.service.ts`)
|
|
**Responsibility**: Data transformation and mapping
|
|
- Transform business data to Salesforce format
|
|
- Apply business rules to field mapping
|
|
- Handle conditional field logic
|
|
|
|
### 5. **ItemBuilder Layer** (`order-item-builder.service.ts`)
|
|
**Responsibility**: Order item creation and pricing
|
|
- Create order line items
|
|
- Handle pricing calculations
|
|
- Manage product metadata
|
|
|
|
## Benefits of This Structure:
|
|
|
|
### ✅ **Single Responsibility Principle**
|
|
- Each service has one clear purpose
|
|
- Easy to test and maintain
|
|
- Clear boundaries
|
|
|
|
### ✅ **Validator as Single Source of Truth**
|
|
- All validation logic in one place
|
|
- Easy to find and modify validation rules
|
|
- Consistent error handling
|
|
|
|
### ✅ **Orchestrator for Workflow Management**
|
|
- Clear sequence of operations
|
|
- Transaction management
|
|
- Error recovery logic
|
|
|
|
### ✅ **Testability**
|
|
- Each layer can be unit tested independently
|
|
- Mock dependencies easily
|
|
- Clear input/output contracts
|
|
|
|
## Implementation Changes:
|
|
|
|
### Move DTO validation to Validator:
|
|
```typescript
|
|
// Before: Controller has DTO validation
|
|
@Body() body: CreateOrderDto
|
|
|
|
// After: Controller accepts any, Validator validates
|
|
@Body() body: any
|
|
```
|
|
|
|
### Enhanced Validator:
|
|
```typescript
|
|
async validateCompleteOrder(userId: string, rawBody: any) {
|
|
// 1. Format validation (was DTO)
|
|
const body = this.validateRequestFormat(rawBody);
|
|
|
|
// 2. Business validation (current)
|
|
const userMapping = await this.validateUserMapping(userId);
|
|
await this.validatePaymentMethod(userId, userMapping.whmcsClientId);
|
|
|
|
// 3. SKU validation (move here)
|
|
const pricebookId = await this.findPricebookId();
|
|
await this.validateSKUs(body.skus, pricebookId);
|
|
this.validateBusinessRules(body.orderType, body.skus);
|
|
|
|
// 4. Order-specific validation
|
|
if (body.orderType === "Internet") {
|
|
await this.validateInternetDuplication(userId, userMapping.whmcsClientId);
|
|
}
|
|
|
|
return { validatedBody: body, userMapping, pricebookId };
|
|
}
|
|
```
|
|
|
|
### Simplified Orchestrator:
|
|
```typescript
|
|
async createOrder(userId: string, rawBody: any) {
|
|
// 1. Complete validation
|
|
const { validatedBody, userMapping, pricebookId } =
|
|
await this.validator.validateCompleteOrder(userId, rawBody);
|
|
|
|
// 2. Build order
|
|
const orderFields = this.builder.buildOrderFields(validatedBody, userMapping, pricebookId);
|
|
|
|
// 3. Create in Salesforce
|
|
const created = await this.sf.sobject("Order").create(orderFields);
|
|
|
|
// 4. Create items
|
|
await this.itemBuilder.createOrderItemsFromSKUs(created.id, validatedBody.skus, pricebookId);
|
|
|
|
return { sfOrderId: created.id, status: "Created" };
|
|
}
|
|
```
|