Assist_Design/ORDERS-ARCHITECTURE-REVIEW.md

415 lines
13 KiB
Markdown

# 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)
```typescript
// 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:**
```typescript
// 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)
---
## 📋 Recommended Architecture
### **Domain Layer** (`packages/domain/orders/`)
**Should contain:**
```typescript
// 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:**
```typescript
// 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:**
```typescript
// 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`
```typescript
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`
```typescript
/**
* 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`
```typescript
export function buildOrderSelectFields(...)
export function buildOrderItemSelectFields(...)
export function buildOrderItemProduct2Fields(...)
```
**TO:** `apps/bff/src/integrations/salesforce/utils/order-query-builder.ts`
```typescript
/**
* 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`
```typescript
@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)
---
## 🎯 Recommended Action Plan
### **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.