Assist_Design/ORDERS-ARCHITECTURE-REVIEW.md

13 KiB

Orders Domain & BFF Integration - Architecture Review

Executive Summary

After comprehensive review, there are significant architectural issues that need addressing. The current structure mixes infrastructure concerns with business logic, has types in wrong locations, and the field configuration system is problematic.


🚨 Critical Issues Found

1. Field Configuration in Domain (WRONG PLACE)

Issue: SalesforceOrdersFieldConfig interfaces are defined in the domain package.

Location: packages/domain/orders/contract.ts (lines 98-177)

// Currently in domain - THIS IS WRONG
export interface SalesforceOrderMnpFieldConfig { ... }
export interface SalesforceOrderFieldConfig { ... }
export interface SalesforceOrdersFieldConfig { ... }

Why This Is Wrong:

  • These are infrastructure configuration, not business types
  • They map logical field names to Salesforce custom field API names
  • They're deployment-specific (which fields exist in which Salesforce org)
  • They're NOT business concepts - customers don't care about field mappings
  • Domain should be provider-agnostic

What Should Happen:

  • Domain defines: "Here are the Salesforce field names you must use"
  • Domain defines: "Here are the business concepts"
  • BFF/Integration layer: "Here's how we map those concepts to Salesforce"

Correct Location: apps/bff/src/integrations/salesforce/types/field-config.types.ts


2. SalesforceFieldConfigService Location (CORRECT NOW)

Current Location: apps/bff/src/integrations/salesforce/services/salesforce-field-config.service.ts

Status: This is CORRECT! You were right to question it.

Why:

  • Reads from environment variables (ConfigService)
  • Deployment/environment specific
  • Infrastructure concern, not business logic
  • Belongs in BFF integration layer

3. Pub/Sub Event Types (CORRECT)

Current Location: apps/bff/src/integrations/salesforce/types/pubsub-events.types.ts

Status: This is CORRECT!

Why:

  • Salesforce-specific infrastructure types
  • Platform event structure, not business events
  • Integration concern, not domain concern
  • Belongs in BFF integration layer

4. Type Duplication & Confusion

Found Multiple Similar Types:

// In BFF order services
export interface OrderItemMappingResult { ... }          // order-whmcs-mapper.service.ts
export interface OrderFulfillmentStep { ... }            // order-fulfillment-orchestrator.service.ts
export interface OrderFulfillmentContext { ... }         // order-fulfillment-orchestrator.service.ts
export interface SimFulfillmentRequest { ... }           // sim-fulfillment.service.ts

// In Domain
export type FulfillmentOrderProduct { ... }
export type FulfillmentOrderItem { ... }
export type FulfillmentOrderDetails { ... }

Issue: Clear separation between:

  • Business/Domain types (what an order is)
  • Workflow/Process types (how fulfillment works)
  • Integration types (how we talk to external systems)

Domain Layer (packages/domain/orders/)

Should contain:

// Business entities
- OrderDetails
- OrderSummary
- OrderItemDetails
- OrderItemSummary
- FulfillmentOrderDetails (if this is a business concept)

// Business rules
- Order validation schemas
- Business constants (ORDER_TYPE, ORDER_STATUS, etc.)
- Order lifecycle states

// Provider contracts (interfaces only, no config)
- What data structure do Salesforce/WHMCS need?
- NOT which fields to use

Should NOT contain:

  • Field configuration interfaces
  • Environment-specific mappings
  • Query helpers (buildOrderSelectFields, etc.)
  • Integration-specific types

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

Should contain:

// Infrastructure configuration
- SalesforceFieldConfigService  (already here)
- Field mapping types (currently in domain )
- Query builders (should move from domain)
- Connection management
- Pub/Sub types  (already here)

// Integration adapters
- Transform business types  Salesforce records
- Transform Salesforce records  business types

Structure:

apps/bff/src/integrations/salesforce/
├── services/
│   ├── salesforce-connection.service.ts
│   ├── salesforce-field-config.service.ts  ✅
│   ├── salesforce-account.service.ts
│   └── salesforce-order.service.ts         ← NEW (extract from modules/orders)
├── types/
│   ├── field-config.types.ts               ← MOVE HERE from domain
│   ├── pubsub-events.types.ts              ✅
│   └── query-builder.types.ts              ← NEW
└── utils/
    ├── soql.util.ts
    └── query-builder.util.ts                ← MOVE buildOrderSelectFields here

BFF Order Module (apps/bff/src/modules/orders/)

Should contain:

// HTTP/API layer
- OrdersController (API endpoints)

// Application services (orchestration)
- OrderOrchestrator (coordinates everything)
- OrderValidator (business + integration validation)
- OrderFulfillmentOrchestrator

// Workflow types (NOT business types)
- OrderFulfillmentContext
- OrderFulfillmentStep
- SimFulfillmentRequest

Should NOT directly call:

  • Direct Salesforce queries (use SalesforceOrderService)
  • Field mapping logic (use SalesforceFieldConfigService)

🔧 Specific Refactorings Needed

1. Move Field Config Types Out of Domain

FROM: packages/domain/orders/contract.ts

export interface SalesforceOrderMnpFieldConfig { ... }
export interface SalesforceOrderBillingFieldConfig { ... }
export interface SalesforceOrderFieldConfig { ... }
export interface SalesforceOrderItemFieldConfig { ... }
export interface SalesforceOrdersFieldConfig { ... }

TO: apps/bff/src/integrations/salesforce/types/field-config.types.ts

/**
 * Salesforce Field Configuration Types
 * Maps logical business field names to Salesforce custom field API names
 */
export interface SalesforceOrderFieldConfig { ... }
export interface SalesforceOrdersFieldConfig { ... }
// etc.

2. Move Query Builders Out of Domain

FROM: packages/domain/orders/providers/salesforce/query.ts

export function buildOrderSelectFields(...)
export function buildOrderItemSelectFields(...)
export function buildOrderItemProduct2Fields(...)

TO: apps/bff/src/integrations/salesforce/utils/order-query-builder.ts

/**
 * SOQL Query builders for Orders
 * Uses field configuration to build dynamic queries
 */
export function buildOrderSelectFields(...)
export function buildOrderItemSelectFields(...)
// etc.

Why: These are SOQL-specific, infrastructure concerns, not business logic.


3. Create SalesforceOrderService

NEW: apps/bff/src/integrations/salesforce/services/salesforce-order.service.ts

@Injectable()
export class SalesforceOrderService {
  constructor(
    private connection: SalesforceConnection,
    private fieldConfig: SalesforceFieldConfigService,
    private logger: Logger
  ) {}

  async getOrderById(orderId: string): Promise<OrderDetails | null> {
    // Contains Salesforce-specific query logic
    // Uses field config to build queries
    // Transforms SF records → domain types
  }

  async createOrder(orderData: CreateOrderRequest): Promise<{ id: string }> {
    // Transform domain → SF record
    // Create in Salesforce
  }

  async listOrders(params: OrderQueryParams): Promise<OrderSummary[]> {
    // Query logic
  }
}

Benefits:

  • Encapsulates all Salesforce order operations
  • OrderOrchestrator doesn't need to know about Salesforce internals
  • Easier to test
  • Easier to swap providers

4. Consolidate WHMCS Mapping

Current: Duplicate mapping logic in:

  • packages/domain/orders/providers/whmcs/mapper.ts (domain )
  • apps/bff/src/modules/orders/services/order-whmcs-mapper.service.ts (BFF wrapper)

Issue: The service is just a thin wrapper that calls domain mapper. Either:

Option A: Keep mapper in domain, remove BFF service Option B: Move mapper to BFF integration layer

Recommendation: Option A - Domain mapper is fine for data transformation.


📊 Layering Principles

┌─────────────────────────────────────────┐
│     HTTP Layer (Controller)             │
│  - API endpoints                        │
│  - Request/Response formatting          │
└──────────────────┬──────────────────────┘
                   │
┌──────────────────▼──────────────────────┐
│   Application Layer (Orchestrators)     │
│  - OrderOrchestrator                    │
│  - OrderFulfillmentOrchestrator         │
│  - Workflow coordination                │
└──────────────────┬──────────────────────┘
                   │
      ┌────────────┴────────────┐
      │                         │
┌─────▼──────────┐    ┌────────▼──────────┐
│  Domain Layer  │    │ Integration Layer │
│  (Business)    │    │ (Infrastructure)  │
│                │    │                   │
│ - OrderDetails │    │ - SF Order Service│
│ - Validation   │    │ - WHMCS Service   │
│ - Rules        │    │ - Field Config    │
│ - Schemas      │    │ - Query Builders  │
└────────────────┘    └───────────────────┘

Rules:

  1. Domain can depend on: Nothing external
  2. Integration can depend on: Domain
  3. Application can depend on: Domain + Integration
  4. HTTP can depend on: Application

Anti-patterns currently present:

  • Domain exports query builders (infrastructure concern)
  • Domain defines field configuration types (deployment concern)
  • Application services directly query Salesforce (should go through integration service)

Phase 1: Move Infrastructure Types Out of Domain (High Priority)

  1. Create apps/bff/src/integrations/salesforce/types/field-config.types.ts
  2. Move all Salesforce*FieldConfig interfaces from domain to BFF
  3. Update imports across codebase
  4. Update SalesforceFieldConfigService to export types from new location

Phase 2: Extract Salesforce Order Service (Medium Priority)

  1. Create SalesforceOrderService in integration layer
  2. Move all Salesforce query logic from OrderOrchestrator to new service
  3. Move query builders from domain to integration utils
  4. Update orchestrator to use new service

Phase 3: Clean Up Type Exports (Medium Priority)

  1. Review all domain exports
  2. Remove infrastructure types from domain exports
  3. Create clear separation: business types vs workflow types vs integration types
  4. Document what belongs where

Phase 4: Consolidate Mappers (Low Priority)

  1. Decide: Keep WHMCS mappers in domain or move to integration
  2. Remove redundant service wrappers
  3. Standardize mapper patterns

💡 Key Insights

Your Questions Answered:

  1. "Should SalesforceFieldConfigService be in domain or BFF?"

    • Answer: BFF integration layer (already correct!)
    • It's environment-specific configuration, not business logic
  2. "Should PubSub types be in domain?"

    • Answer: No, BFF integration layer (already correct!)
    • They're Salesforce platform events, not business events
  3. "There are so many overlapping types and wrong used"

    • Answer: YES! Field config interfaces should NOT be in domain
    • Query builders should NOT be in domain
    • These are infrastructure concerns masquerading as business logic

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. SalesforceFieldConfigService is in BFF (correct location)
  5. Pub/Sub types are in BFF (correct location)
  6. Raw provider types (SalesforceOrderRecord) are in domain (for portability)

🎓 Architecture Philosophy

Domain Layer:

"What does the business care about? What are the rules?"

Integration Layer:

"How do we talk to external systems? How do we map our concepts to theirs?"

Application Layer:

"What workflows do we support? How do we coordinate services?"

HTTP Layer:

"How do we expose functionality via API?"


Conclusion

The order domain needs refactoring to properly separate:

  • Business logic (domain)
  • Infrastructure configuration (BFF integration)
  • Workflow orchestration (BFF application)

The most critical issue is that field configuration types are in the domain when they should be in the BFF integration layer. This violates domain-driven design principles and creates unwanted coupling.

The good news: You were 100% right to question this, and the major pieces (service locations) are already correct. We just need to move the type definitions to match.