312 lines
9.9 KiB
Markdown
312 lines
9.9 KiB
Markdown
# 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
|
|
|
|
```typescript
|
|
// 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
|
|
|
|
```typescript
|
|
// 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:
|
|
```typescript
|
|
// 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
|
|
|
|
```typescript
|
|
// 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
|
|
|
|
```typescript
|
|
// 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
|
|
|
|
---
|
|
|
|
## 📖 Related Documentation
|
|
|
|
- [BFF Integration Patterns](./docs/BFF-INTEGRATION-PATTERNS.md)
|
|
- [Domain Package README](./packages/domain/README.md)
|
|
- [Schema-First Approach](./packages/domain/SCHEMA-FIRST-COMPLETE.md)
|
|
|
|
---
|
|
|
|
## 🎯 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
|
|
|