415 lines
13 KiB
Markdown
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.
|
|
|