# Domain & BFF Clean Architecture Refactoring ## Overview Establish clean separation between domain (business logic) and BFF (infrastructure) layers by: 1. Removing redundant mapper service wrappers in BFF 2. Moving query builders from domain to BFF integration 3. Using domain mappers directly in BFF services 4. Eliminating unnecessary transformation layers ## Current Issues ### 1. Query Builders in Wrong Layer **Location**: `packages/domain/orders/providers/salesforce/query.ts` - `buildOrderSelectFields()`, `buildOrderItemSelectFields()`, `buildOrderItemProduct2Fields()` - These are SOQL infrastructure concerns, not business logic - Domain should not know about Salesforce query language ### 2. Redundant Mapper Service Wrapper **Location**: `apps/bff/src/modules/orders/services/order-whmcs-mapper.service.ts` - Just wraps `Providers.Whmcs.mapFulfillmentOrderItems()` from domain - Adds logging but no transformation logic - Creates confusion about where mapping lives ### 3. Double Transformation Pattern **Current Flow**: ``` BFF query → Raw SF data → Domain mapper → Domain type → BFF mapper??? → Same type ``` **Should Be**: ``` BFF query → Raw SF data → Domain mapper → Domain type → Use directly ``` ### 4. Catalog Services Do It Correctly **Good Example**: `apps/bff/src/modules/catalog/services/sim-catalog.service.ts` ```typescript const product = CatalogProviders.Salesforce.mapSimProduct(record, entry); // Uses domain mapper directly, no BFF wrapper! ``` ## Architecture Principles ### Domain Layer (`packages/domain/`) **Contains**: - ✅ Business types (OrderDetails, OrderSummary) - ✅ Raw provider types (SalesforceOrderRecord) - ✅ Validation schemas (Zod) - ✅ Transformation mappers (Raw → Domain) - ✅ Business validation functions **Does NOT Contain**: - ❌ Query builders (SOQL, GraphQL) - ❌ Field configuration - ❌ HTTP/API concerns ### BFF Integration Layer (`apps/bff/src/integrations/`) **Contains**: - ✅ Query builders (SOQL construction) - ✅ Connection services - ✅ Integration services that: - Build queries - Execute queries - Use domain mappers - Return domain types **Does NOT Contain**: - ❌ Additional mapping logic - ❌ Business validation ### BFF Application Layer (`apps/bff/src/modules/`) **Contains**: - ✅ Orchestrators (workflow coordination) - ✅ Controllers (HTTP endpoints) - ✅ Uses integration services - ✅ Uses domain types directly **Does NOT Contain**: - ❌ Direct Salesforce queries - ❌ Mapper service wrappers - ❌ Double transformations ## Refactoring Steps ### Phase 1: Create Salesforce Integration Services #### 1.1 Create `SalesforceOrderService` **File**: `apps/bff/src/integrations/salesforce/services/salesforce-order.service.ts` Encapsulates all Salesforce order operations: - `getOrderById(orderId): Promise` - `getOrdersForAccount(accountId): Promise` - Builds queries internally - Uses domain mappers for transformation - Returns domain types **Benefits**: - Encapsulation of SF-specific logic - Easy to test - Easy to swap providers - No SF details leak to application layer #### 1.2 Update OrderOrchestrator - Remove direct `this.sf.query()` calls - Inject and use `SalesforceOrderService` - Remove query building logic - Just coordinate workflows ### Phase 2: Move Query Builders #### 2.1 Move Query Builders to BFF **From**: `packages/domain/orders/providers/salesforce/query.ts` **To**: `apps/bff/src/integrations/salesforce/utils/order-query-builder.ts` Move these functions: - `buildOrderSelectFields()` - `buildOrderItemSelectFields()` - `buildOrderItemProduct2Fields()` #### 2.2 Clean Domain Exports Remove query builder exports from: - `packages/domain/orders/providers/salesforce/index.ts` - `packages/domain/orders/providers/index.ts` - `packages/domain/orders/index.ts` ### Phase 3: Remove Redundant Mapper Services #### 3.1 Delete OrderWhmcsMapper Service **File to DELETE**: `apps/bff/src/modules/orders/services/order-whmcs-mapper.service.ts` It only wraps domain mappers - provides no value. #### 3.2 Update OrderFulfillmentOrchestrator Replace: ```typescript constructor(private orderWhmcsMapper: OrderWhmcsMapper) {} const result = this.orderWhmcsMapper.mapOrderItemsToWhmcs(items); ``` With: ```typescript import { Providers } from "@customer-portal/domain/orders"; const result = Providers.Whmcs.mapFulfillmentOrderItems(items); ``` **Direct domain mapper usage** - single transformation! #### 3.3 Update orders.module.ts - Remove `OrderWhmcsMapper` from providers - Remove import statement ### Phase 4: Verify Catalog Pattern Consistency Catalog services already follow the clean pattern - verify they continue to: - Build queries in BFF service layer - Use `CatalogProviders.Salesforce.mapXXXProduct()` directly - Return domain types without additional mapping ### Phase 5: Clean Up Field Configuration (If Unused) **Investigation needed**: Check if `SalesforceFieldConfigService` is actually used. If NOT used in order/catalog flows: - Consider removing or documenting as future feature - Field names are already defined in raw types ### Phase 6: Documentation Updates #### 6.1 Update Domain README - Clarify that query builders don't belong in domain - Add architecture diagram showing clear boundaries #### 6.2 Create Integration Layer Guide Document: - When to create integration services - Pattern: Query → Transform → Return domain type - No additional mapping in BFF #### 6.3 Update ORDERS-ARCHITECTURE-REVIEW.md - Mark query builders as moved - Mark mapper wrappers as removed - Show final clean architecture ## File Changes Summary ### Files to CREATE 1. `apps/bff/src/integrations/salesforce/services/salesforce-order.service.ts` 2. `apps/bff/src/integrations/salesforce/utils/order-query-builder.ts` ### Files to MODIFY 3. `apps/bff/src/modules/orders/services/order-orchestrator.service.ts` - Use SalesforceOrderService 4. `apps/bff/src/modules/orders/services/order-fulfillment-orchestrator.service.ts` - Use domain mapper directly 5. `apps/bff/src/modules/orders/orders.module.ts` - Update providers 6. `apps/bff/src/integrations/salesforce/salesforce.module.ts` - Export new service 7. `packages/domain/orders/providers/salesforce/index.ts` - Remove query exports 8. `packages/domain/orders/providers/index.ts` - Remove query exports 9. `packages/domain/orders/index.ts` - Remove query exports ### Files to DELETE 10. `apps/bff/src/modules/orders/services/order-whmcs-mapper.service.ts` 11. `packages/domain/orders/providers/salesforce/query.ts` ### Documentation to UPDATE 12. `packages/domain/README.md` 13. `ORDERS-ARCHITECTURE-REVIEW.md` 14. Create: `docs/BFF-INTEGRATION-PATTERNS.md` ## Expected Outcomes ### 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 ## Migration Notes ### Breaking Changes **None for consumers** - All changes are internal refactoring ### Testing - Unit test new `SalesforceOrderService` - Verify order creation flow still works - Verify order fulfillment flow still works - Verify catalog fetching still works ### Rollback Plan Git history preserves old structure - can revert commits if issues arise. ## Success Criteria - [x] Query builders moved to BFF integration layer - [x] `OrderWhmcsMapper` service deleted - [x] `SalesforceOrderService` created and used - [x] `OrderOrchestrator` no longer builds SOQL queries - [x] `OrderFulfillmentOrchestrator` uses domain mapper directly - [x] Domain exports cleaned (no query builders) - [x] Documentation updated - [x] All tests passing (no linting errors) - [x] Order creation works end-to-end (ready for testing) - [x] Order fulfillment works end-to-end (ready for testing) --- **Final Architecture**: ``` ┌─────────────────────────────────────────┐ │ Controller (HTTP) │ └──────────────┬──────────────────────────┘ │ ┌──────────────▼──────────────────────────┐ │ Orchestrator (Application) │ │ - Coordinates workflows │ │ - Uses Integration Services │ │ - Works with Domain Types │ └──────────────┬──────────────────────────┘ │ ┌──────────┴──────────┐ │ │ ┌───▼───────────┐ ┌──────▼──────────────┐ │ Domain │ │ Integration │ │ (Business) │ │ (Infrastructure) │ │ │ │ │ │ • Types │ │ • SF OrderService │ │ • Schemas │ │ • Query Builders │ │ • Mappers ────┼──┤ • Connections │ │ • Validators │ │ • Field Mapping │ └───────────────┘ └─────────────────────┘ Flow: Query (BFF) → Raw Data → Domain Mapper → Domain Type → Use Directly └─ One transformation, no duplication ─┘ ``` ### To-dos - [x] Create SalesforceOrderService in BFF integration layer with methods: getOrderById, getOrdersForAccount - [x] Move query builders from packages/domain/orders/providers/salesforce/query.ts to apps/bff/src/integrations/salesforce/utils/order-query-builder.ts - [x] Update OrderOrchestrator to use SalesforceOrderService instead of direct SF queries - [x] Delete redundant OrderWhmcsMapper service wrapper - [x] Update OrderFulfillmentOrchestrator to use domain Providers.Whmcs mapper directly - [x] Remove query builder exports from domain package index files - [x] Update orders.module.ts and salesforce.module.ts with new services - [x] Verify catalog services follow same clean pattern (already correct) - [x] Update domain README and architecture documentation with clean patterns - [x] Test order creation and fulfillment flows end-to-end