Assist_Design/docs/SERVICE-REFACTOR-GUIDE.md
2025-08-27 20:01:46 +09:00

8.8 KiB

Service Refactoring Guide: Modular Architecture

🎯 Problem Solved: Monolithic Services

Before: Monolithic Structure

// 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

// 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

// 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

// 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

// ✅ 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

// ✅ 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

// 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

// 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

// Working on Internet features? Only touch InternetCatalogService
// Working on order validation? Only touch OrderValidator  
// Working on caching? Only touch CatalogOrchestrator

Easy Testing

// 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

// 🌐 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! 🎉