Assist_Design/ORDERS-ARCHITECTURE-REVIEW.md

9.9 KiB

Orders Domain & BFF Integration - Architecture Review

Executive Summary

Date: October 2025
Status: REFACTORED - CLEAN ARCHITECTURE

After comprehensive review and refactoring, the architecture now follows clean separation of concerns with a single source of truth for data transformations.


Refactoring Complete

What Was Fixed

  1. Query Builders Moved to BFF Integration

    • FROM: packages/domain/orders/providers/salesforce/query.ts
    • TO: apps/bff/src/integrations/salesforce/utils/order-query-builder.ts
    • Reason: SOQL is infrastructure, not business logic
  2. SalesforceOrderService Created

    • Location: apps/bff/src/integrations/salesforce/services/salesforce-order.service.ts
    • Encapsulates all Salesforce order operations
    • Uses domain mappers for transformation
    • Returns domain types
  3. Redundant Mapper Service Removed

    • Deleted: apps/bff/src/modules/orders/services/order-whmcs-mapper.service.ts
    • Reason: Only wrapped domain mappers, provided no value
    • Now use Providers.Whmcs.mapFulfillmentOrderItems() directly
  4. OrderOrchestrator Refactored

    • Removed direct Salesforce queries
    • Uses SalesforceOrderService for data fetching
    • Simplified from 150+ lines to clean delegation
  5. Domain Exports Cleaned

    • Removed query builder exports from domain package
    • Domain now only exports business logic

🎯 Current Architecture

Clean Separation of Concerns

┌─────────────────────────────────────────┐
│ Controller (HTTP)                       │
│  - API endpoints                        │
│  - Request/Response formatting          │
└──────────────────┬──────────────────────┘
                   │
┌──────────────────▼──────────────────────┐
│ Orchestrator (Application)              │
│  - Coordinates workflows                │
│  - Uses Integration Services            │
│  - Works with Domain Types              │
└──────────────────┬──────────────────────┘
                   │
    ┌──────────────┴──────────────┐
    │                             │
┌───▼───────────┐  ┌──────────────▼──────────┐
│ Domain        │  │ Integration              │
│ (Business)    │  │ (Infrastructure)         │
│               │  │                          │
│ • Types       │  │ • SalesforceOrderService │
│ • Schemas     │  │ • Query Builders         │
│ • Mappers ────┼──┤ • Connections            │
│ • Validators  │  │ • API Clients            │
└───────────────┘  └──────────────────────────┘

Flow: Query (BFF) → Raw Data → Domain Mapper → Domain Type → Use Directly
      └────────── Single transformation, no duplication ──────────┘

Domain Layer (packages/domain/orders/)

Contains:

  • Business types (OrderDetails, OrderSummary)
  • Raw provider types (SalesforceOrderRecord)
  • Validation schemas (Zod)
  • Transformation mappers (Raw → Domain)
  • Business validation functions

Does NOT Contain:

  • Query builders (moved to BFF)
  • Field configuration
  • HTTP/API concerns

Integration Layer (apps/bff/src/integrations/salesforce/)

Contains:

  • SalesforceOrderService - Encapsulates order operations
  • Query builders (order-query-builder.ts)
  • Connection services
  • Uses domain mappers for transformation

Does NOT Contain:

  • Additional mapping logic (uses domain mappers)
  • Business validation

Application Layer (apps/bff/src/modules/orders/)

Contains:

  • OrderOrchestrator - Workflow coordination
  • OrderFulfillmentOrchestrator - Fulfillment workflows
  • Controllers (HTTP endpoints)
  • Uses integration services
  • Uses domain mappers directly

Does NOT Contain:

  • Direct Salesforce queries
  • Mapper service wrappers (deleted)
  • Double transformations

📊 Key Improvements

Before: Mixed Concerns

// OrderOrchestrator building SOQL directly (❌ Wrong)
const soql = `SELECT ${buildOrderSelectFields().join(", ")} FROM Order...`;
const result = await this.sf.query(soql);
const order = DomainMapper.transform(result);

// OrderWhmcsMapper wrapping domain mapper (❌ Redundant)
mapOrderItemsToWhmcs(items) {
  return Providers.Whmcs.mapFulfillmentOrderItems(items);
}

After: Clean Architecture

// OrderOrchestrator delegates to integration service (✅ Correct)
return this.salesforceOrderService.getOrderById(orderId);

// Direct domain mapper usage (✅ Clean)
const whmcsItems = Providers.Whmcs.mapFulfillmentOrderItems(items);

Benefits Achieved

Architecture Cleanliness

  • Single source of truth for transformations (domain mappers)
  • Clear separation: domain = business, BFF = infrastructure
  • No redundant mapping layers
  • Query logic in correct layer (BFF integration)

Code Quality

  • Easier to test (clear boundaries)
  • Easier to maintain (no duplication)
  • Easier to understand (one transformation path)
  • Easier to swap providers (integration services encapsulate)

Developer Experience

  • Clear patterns to follow
  • No confusion about where code goes
  • Consistent with catalog services
  • Self-documenting architecture

📁 File Structure

Created Files

  1. apps/bff/src/integrations/salesforce/services/salesforce-order.service.ts
  2. apps/bff/src/integrations/salesforce/utils/order-query-builder.ts
  3. docs/BFF-INTEGRATION-PATTERNS.md

Modified Files

  1. apps/bff/src/modules/orders/services/order-orchestrator.service.ts
  2. apps/bff/src/modules/orders/services/order-fulfillment-orchestrator.service.ts
  3. apps/bff/src/modules/orders/orders.module.ts
  4. apps/bff/src/integrations/salesforce/salesforce.module.ts
  5. packages/domain/orders/providers/salesforce/index.ts
  6. packages/domain/orders/index.ts

Deleted Files

  1. apps/bff/src/modules/orders/services/order-whmcs-mapper.service.ts
  2. packages/domain/orders/providers/salesforce/query.ts

🎓 Architecture Principles Followed

Single Transformation Principle

One transformation path - raw data flows through domain mapper exactly once:

Query (BFF) → Raw Data → Domain Mapper → Domain Type → Use Directly

Encapsulation Principle

Integration services hide external system complexity:

// Application layer doesn't see Salesforce details
const order = await this.salesforceOrderService.getOrderById(id);

Separation of Concerns

Each layer has a single responsibility:

  • Domain: Business logic, types, validation
  • Integration: External system interaction
  • Application: Workflow coordination
  • HTTP: API endpoints

🔍 Pattern Examples

Example 1: Fetching Orders

// OrderOrchestrator (Application Layer)
async getOrder(orderId: string): Promise<OrderDetails | null> {
  // Clean delegation - no SF-specific code!
  return this.salesforceOrderService.getOrderById(orderId);
}

// SalesforceOrderService (Integration Layer)
async getOrderById(orderId: string): Promise<OrderDetails | null> {
  // 1. Build query (infrastructure)
  const soql = buildOrderQuery(orderId);
  
  // 2. Execute query
  const rawData = await this.sf.query(soql);
  
  // 3. Use domain mapper (single transformation!)
  return Providers.Salesforce.transformSalesforceOrderDetails(rawData);
}

Example 2: Order Fulfillment

// OrderFulfillmentOrchestrator
{
  id: "mapping",
  description: "Map OrderItems to WHMCS format",
  execute: () => {
    // Use domain mapper directly - no service wrapper!
    const result = OrderProviders.Whmcs.mapFulfillmentOrderItems(
      context.orderDetails.items
    );
    return Promise.resolve(result);
  },
}

What's Already Good

  1. Core business types (OrderDetails, OrderSummary) are in domain
  2. Validation schemas are in domain
  3. Business constants (ORDER_TYPE, ORDER_STATUS) are in domain
  4. Domain mappers provide single transformation
  5. Raw provider types (SalesforceOrderRecord) are in domain
  6. Catalog services already follow this pattern


🎯 Future Considerations

Potential Enhancements

  1. Field Configuration: Currently not used - consider removing or implementing consistently
  2. Query Caching: Add caching layer in integration services
  3. Query Optimization: Review SOQL queries for performance
  4. Integration Tests: Add tests for SalesforceOrderService

Monitoring

  • Track order query performance
  • Monitor transformation errors
  • Log integration service calls

Conclusion

Status: COMPLETE

The architecture now follows clean separation of concerns:

  • Domain: Pure business logic (no infrastructure)
  • Integration: External system encapsulation (SF, WHMCS)
  • Application: Workflow coordination (orchestrators)

Key Achievement: Single source of truth for transformations with no redundant mapping layers.


Last Updated: October 2025
Refactored By: Architecture Review Team