274 lines
8.8 KiB
Markdown
274 lines
8.8 KiB
Markdown
|
|
# Service Refactoring Guide: Modular Architecture
|
||
|
|
|
||
|
|
## 🎯 **Problem Solved: Monolithic Services**
|
||
|
|
|
||
|
|
### ❌ **Before: Monolithic Structure**
|
||
|
|
```typescript
|
||
|
|
// CatalogService.ts (700+ lines)
|
||
|
|
class CatalogService {
|
||
|
|
// Internet logic mixed with SIM logic mixed with VPN logic
|
||
|
|
// Caching logic mixed with business logic
|
||
|
|
// Hard to test, maintain, and extend
|
||
|
|
}
|
||
|
|
|
||
|
|
// OrdersService.ts (400+ lines)
|
||
|
|
class OrdersService {
|
||
|
|
// Validation, building, item creation, provisioning all mixed
|
||
|
|
// Difficult to isolate specific functionality
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**Problems:**
|
||
|
|
- ❌ Single Responsibility Principle violated
|
||
|
|
- ❌ Hard to test individual components
|
||
|
|
- ❌ Difficult to maintain and debug
|
||
|
|
- ❌ High coupling between unrelated logic
|
||
|
|
- ❌ Large classes with mixed concerns
|
||
|
|
|
||
|
|
## ✅ **After: Modular Architecture**
|
||
|
|
|
||
|
|
### **🏗️ Catalog Services Structure**
|
||
|
|
|
||
|
|
```
|
||
|
|
📁 catalog/
|
||
|
|
├── 📁 services/
|
||
|
|
│ ├── base-catalog.service.ts # 🔧 Common Salesforce operations
|
||
|
|
│ ├── internet-catalog.service.ts # 🌐 Internet-specific logic
|
||
|
|
│ ├── sim-catalog.service.ts # 📱 SIM-specific logic
|
||
|
|
│ ├── vpn-catalog.service.ts # 🔒 VPN-specific logic
|
||
|
|
│ └── catalog-orchestrator.service.ts # 🎯 Coordinates everything + caching
|
||
|
|
├── catalog.controller.ts # 🚀 Routes to orchestrator
|
||
|
|
├── catalog.service.ts # ⚡ Legacy (backward compatible)
|
||
|
|
└── structured-catalog.service.ts # ⚡ Legacy (backward compatible)
|
||
|
|
```
|
||
|
|
|
||
|
|
### **🏗️ Orders Services Structure**
|
||
|
|
|
||
|
|
```
|
||
|
|
📁 orders/
|
||
|
|
├── 📁 services/
|
||
|
|
│ ├── order-validator.service.ts # ✅ Validation logic only
|
||
|
|
│ ├── order-builder.service.ts # 🔨 Order header building
|
||
|
|
│ ├── order-item-builder.service.ts # 📦 OrderItem creation
|
||
|
|
│ └── order-orchestrator.service.ts # 🎯 Coordinates order flow
|
||
|
|
├── orders.controller.ts # 🚀 Routes to orchestrator
|
||
|
|
└── orders.service.ts # ⚡ Legacy (backward compatible)
|
||
|
|
```
|
||
|
|
|
||
|
|
## 🧩 **Service Responsibilities**
|
||
|
|
|
||
|
|
### **Catalog Services**
|
||
|
|
|
||
|
|
| Service | Responsibility | Lines | Dependencies |
|
||
|
|
|---------|---------------|-------|-------------|
|
||
|
|
| `BaseCatalogService` | Common Salesforce queries, field mapping, error handling | ~120 | SF, Logger |
|
||
|
|
| `InternetCatalogService` | Internet plans, installations, add-ons logic | ~180 | BaseCatalog |
|
||
|
|
| `SimCatalogService` | SIM plans, activation fees, add-ons logic | ~140 | BaseCatalog |
|
||
|
|
| `VpnCatalogService` | VPN plans, activation fees logic | ~100 | BaseCatalog |
|
||
|
|
| `CatalogOrchestrator` | Coordinates services, handles caching | ~120 | All catalog services |
|
||
|
|
|
||
|
|
### **Order Services**
|
||
|
|
|
||
|
|
| Service | Responsibility | Lines | Dependencies |
|
||
|
|
|---------|---------------|-------|-------------|
|
||
|
|
| `OrderValidator` | Business rule validation, user mapping | ~180 | Mappings, WHMCS, SF |
|
||
|
|
| `OrderBuilder` | Order header field building | ~90 | None |
|
||
|
|
| `OrderItemBuilder` | SKU parsing, OrderItem creation | ~160 | SF |
|
||
|
|
| `OrderOrchestrator` | Coordinates order creation flow | ~120 | All order services |
|
||
|
|
|
||
|
|
## 🎯 **Usage Examples**
|
||
|
|
|
||
|
|
### **Using New Catalog Architecture**
|
||
|
|
```typescript
|
||
|
|
// In your service/controller
|
||
|
|
constructor(
|
||
|
|
private catalogOrchestrator: CatalogOrchestrator,
|
||
|
|
private internetCatalog: InternetCatalogService
|
||
|
|
) {}
|
||
|
|
|
||
|
|
// Get complete structured catalog (cached)
|
||
|
|
const catalog = await this.catalogOrchestrator.getStructuredCatalog();
|
||
|
|
|
||
|
|
// Get only Internet data (cached)
|
||
|
|
const internetData = await this.catalogOrchestrator.getInternetCatalog();
|
||
|
|
|
||
|
|
// Get specific Internet plans directly
|
||
|
|
const plans = await this.internetCatalog.getPlans();
|
||
|
|
```
|
||
|
|
|
||
|
|
### **Using New Order Architecture**
|
||
|
|
```typescript
|
||
|
|
// In your controller
|
||
|
|
constructor(private orderOrchestrator: OrderOrchestrator) {}
|
||
|
|
|
||
|
|
// Create order using orchestrated flow
|
||
|
|
const result = await this.orderOrchestrator.createOrder(userId, orderData);
|
||
|
|
|
||
|
|
// Get user orders
|
||
|
|
const orders = await this.orderOrchestrator.getOrdersForUser(userId);
|
||
|
|
```
|
||
|
|
|
||
|
|
### **Testing Individual Components**
|
||
|
|
```typescript
|
||
|
|
// Test only Internet catalog logic
|
||
|
|
describe('InternetCatalogService', () => {
|
||
|
|
let service: InternetCatalogService;
|
||
|
|
let mockBaseCatalog: jest.Mocked<BaseCatalogService>;
|
||
|
|
|
||
|
|
beforeEach(() => {
|
||
|
|
const module = Test.createTestingModule({
|
||
|
|
providers: [
|
||
|
|
InternetCatalogService,
|
||
|
|
{ provide: BaseCatalogService, useValue: mockBaseCatalog }
|
||
|
|
]
|
||
|
|
});
|
||
|
|
service = module.get(InternetCatalogService);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('should parse internet plans correctly', async () => {
|
||
|
|
// Test only internet-specific logic, isolated
|
||
|
|
});
|
||
|
|
});
|
||
|
|
```
|
||
|
|
|
||
|
|
## 🔄 **Backward Compatibility**
|
||
|
|
|
||
|
|
### **Clean API Endpoints**
|
||
|
|
```typescript
|
||
|
|
// ✅ Clean architecture endpoints
|
||
|
|
GET /api/catalog // Uses CatalogOrchestrator (legacy format)
|
||
|
|
GET /api/catalog/structured // Uses CatalogOrchestrator (recommended)
|
||
|
|
GET /api/catalog/internet/addons // Uses CatalogOrchestrator
|
||
|
|
|
||
|
|
// ✅ Clean order endpoints
|
||
|
|
POST /api/orders // Uses OrderOrchestrator
|
||
|
|
GET /api/orders/user // Uses OrderOrchestrator
|
||
|
|
GET /api/orders/:id // Uses OrderOrchestrator
|
||
|
|
```
|
||
|
|
|
||
|
|
### **Clean Architecture Implementation**
|
||
|
|
```typescript
|
||
|
|
// ✅ Final clean implementation - no legacy code
|
||
|
|
@Module({
|
||
|
|
providers: [
|
||
|
|
BaseCatalogService, // Common operations
|
||
|
|
CatalogOrchestrator, // Main coordinator
|
||
|
|
InternetCatalogService, // Focused responsibility
|
||
|
|
SimCatalogService, // Focused responsibility
|
||
|
|
VpnCatalogService, // Focused responsibility
|
||
|
|
]
|
||
|
|
})
|
||
|
|
|
||
|
|
// ✅ Controller uses only orchestrator
|
||
|
|
@Controller()
|
||
|
|
class CatalogController {
|
||
|
|
constructor(
|
||
|
|
private catalogOrchestrator: CatalogOrchestrator // Single clean interface
|
||
|
|
) {}
|
||
|
|
|
||
|
|
@Get() getCatalog() { return this.catalogOrchestrator.getLegacyCatalog(); }
|
||
|
|
@Get('structured') getStructured() { return this.catalogOrchestrator.getStructuredCatalog(); }
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
## 📊 **Benefits Achieved**
|
||
|
|
|
||
|
|
| Aspect | Before | After |
|
||
|
|
|--------|---------|--------|
|
||
|
|
| **Service Size** | 700+ lines | ~120 lines per service |
|
||
|
|
| **Testability** | Hard (monolithic) | Easy (focused) |
|
||
|
|
| **Maintainability** | Difficult (mixed concerns) | Simple (single responsibility) |
|
||
|
|
| **Performance** | No specialized caching | Service-specific caching |
|
||
|
|
| **Extensibility** | Break existing code | Add new services cleanly |
|
||
|
|
| **Debugging** | Search through 700+ lines | Check specific 120-line service |
|
||
|
|
| **Code Coupling** | High (everything connected) | Low (focused dependencies) |
|
||
|
|
|
||
|
|
## 🚀 **Extension Examples**
|
||
|
|
|
||
|
|
### **Adding New Product Type**
|
||
|
|
```typescript
|
||
|
|
// 1. Create focused service
|
||
|
|
@Injectable()
|
||
|
|
export class IotCatalogService extends BaseCatalogService {
|
||
|
|
async getDevices(): Promise<IotDevice[]> {
|
||
|
|
const soql = this.buildCatalogServiceQuery('IoT');
|
||
|
|
// IoT-specific logic only, ~100 lines
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// 2. Add to orchestrator
|
||
|
|
@Injectable()
|
||
|
|
export class CatalogOrchestrator {
|
||
|
|
constructor(
|
||
|
|
// ... existing services
|
||
|
|
private iotCatalog: IotCatalogService // Add new service
|
||
|
|
) {}
|
||
|
|
|
||
|
|
async getStructuredCatalog() {
|
||
|
|
const [internet, sim, vpn, iot] = await Promise.all([
|
||
|
|
this.internetCatalog.getCatalogData(),
|
||
|
|
this.simCatalog.getCatalogData(),
|
||
|
|
this.vpnCatalog.getCatalogData(),
|
||
|
|
this.iotCatalog.getCatalogData() // Add new data
|
||
|
|
]);
|
||
|
|
|
||
|
|
return { internet, sim, vpn, iot };
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// 3. Zero changes to existing services! 🎉
|
||
|
|
```
|
||
|
|
|
||
|
|
### **Adding New Order Validation**
|
||
|
|
```typescript
|
||
|
|
// Add to OrderValidator - single responsibility
|
||
|
|
@Injectable()
|
||
|
|
export class OrderValidator {
|
||
|
|
validateCreditCheck(orderData: any): void {
|
||
|
|
// Add new validation logic without touching other services
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// OrderOrchestrator automatically uses new validation
|
||
|
|
// No changes needed elsewhere! 🎉
|
||
|
|
```
|
||
|
|
|
||
|
|
## 🔧 **Development Benefits**
|
||
|
|
|
||
|
|
### **Focused Development**
|
||
|
|
```typescript
|
||
|
|
// Working on Internet features? Only touch InternetCatalogService
|
||
|
|
// Working on order validation? Only touch OrderValidator
|
||
|
|
// Working on caching? Only touch CatalogOrchestrator
|
||
|
|
```
|
||
|
|
|
||
|
|
### **Easy Testing**
|
||
|
|
```typescript
|
||
|
|
// Test only what you're working on
|
||
|
|
describe('InternetCatalogService', () => {
|
||
|
|
// Mock only BaseCatalogService
|
||
|
|
// Test only Internet logic
|
||
|
|
// ~50 test lines vs 200+ before
|
||
|
|
});
|
||
|
|
```
|
||
|
|
|
||
|
|
### **Clear Code Ownership**
|
||
|
|
```typescript
|
||
|
|
// 🌐 Internet features → InternetCatalogService
|
||
|
|
// 📱 SIM features → SimCatalogService
|
||
|
|
// 🔒 VPN features → VpnCatalogService
|
||
|
|
// ✅ Order validation → OrderValidator
|
||
|
|
// 📦 Order items → OrderItemBuilder
|
||
|
|
```
|
||
|
|
|
||
|
|
## 🎯 **Implementation Complete**
|
||
|
|
|
||
|
|
✅ **Clean Architecture Achieved:**
|
||
|
|
1. **Modular services** with single responsibilities
|
||
|
|
2. **Shared types** eliminating duplication
|
||
|
|
3. **Reusable utilities** for business logic
|
||
|
|
4. **Clean API endpoints** using orchestrators
|
||
|
|
5. **Easy testing** with focused components
|
||
|
|
|
||
|
|
The clean architecture provides excellent maintainability, testability, and extensibility! 🎉
|