# Domain Violations Report **Customer Portal - BFF and Portal Domain Violations Analysis** --- ## Executive Summary This report identifies cases where the BFF (Backend for Frontend) and Portal applications are not following the domain package as the single source of truth for types, validation, and business logic. The analysis reveals several categories of violations that need to be addressed to maintain clean architecture principles. ## Architecture Overview The customer portal follows a clean architecture pattern with: - **Domain Package** (`packages/domain/`): Single source of truth for types, schemas, and business logic - **BFF** (`apps/bff/`): NestJS backend that should consume domain types - **Portal** (`apps/portal/`): Next.js frontend that should consume domain types ## Key Findings ### ✅ **Good Practices Found** 1. **Domain Package Structure**: Well-organized with clear separation of concerns 2. **Type Imports**: Most services correctly import domain types 3. **Validation Patterns**: Many components properly extend domain schemas 4. **Provider Adapters**: Clean separation between domain contracts and provider implementations ### ❌ **Domain Violations Identified** ## 1. BFF Layer Violations ### 1.1 Duplicate Validation Schemas **Location**: `apps/bff/src/modules/orders/services/order-fulfillment-validator.service.ts` ```typescript // ❌ VIOLATION: Duplicate validation schema const salesforceAccountIdSchema = z.string().min(1, "Salesforce AccountId is required"); ``` **Issue**: Creating local Zod schemas instead of using domain validation **Impact**: Duplication of validation logic, potential inconsistencies ### 1.2 Infrastructure-Specific Types **Location**: `apps/bff/src/modules/orders/services/order-fulfillment-validator.service.ts` ```typescript // ❌ VIOLATION: BFF-specific interface that could be domain type export interface OrderFulfillmentValidationResult { sfOrder: SalesforceOrderRecord; clientId: number; isAlreadyProvisioned: boolean; whmcsOrderId?: string; } ``` **Issue**: Business logic types defined in BFF instead of domain **Impact**: Business logic scattered across layers ### 1.3 Local Type Definitions **Location**: `apps/bff/src/modules/invoices/services/invoice-retrieval.service.ts` ```typescript // ❌ VIOLATION: Local interface instead of domain type interface UserMappingInfo { userId: string; whmcsClientId: number; } ``` **Issue**: Infrastructure types that could be standardized in domain **Impact**: Inconsistent type definitions across services ### 1.4 Response Schema Definitions **Location**: `apps/bff/src/modules/orders/orders.controller.ts` ```typescript // ❌ VIOLATION: Controller-specific response schema private readonly createOrderResponseSchema = apiSuccessResponseSchema( z.object({ sfOrderId: z.string(), status: z.string(), message: z.string(), }) ); ``` **Issue**: Response schemas defined in controllers instead of domain **Impact**: API contract not centralized ### 1.5 SOQL Utility Schemas **Location**: `apps/bff/src/integrations/salesforce/utils/soql.util.ts` ```typescript // ❌ VIOLATION: Utility-specific schemas const nonEmptyStringSchema = z.string().min(1, "Value cannot be empty").trim(); const soqlFieldNameSchema = z.string().trim().regex(/^[A-Za-z0-9_.]+$/, "Invalid SOQL field name"); ``` **Issue**: Common validation patterns not centralized **Impact**: Repeated validation logic ## 2. Portal Layer Violations ### 2.1 Component-Specific Types **Location**: `apps/portal/src/features/catalog/components/base/PricingDisplay.tsx` ```typescript // ❌ VIOLATION: UI-specific types that could be domain types export interface PricingTier { name: string; price: number; billingCycle: "Monthly" | "Onetime" | "Annual"; description?: string; features?: string[]; isRecommended?: boolean; originalPrice?: number; } ``` **Issue**: Business concepts defined in UI components **Impact**: Business logic in presentation layer ### 2.2 Checkout-Specific Types **Location**: `apps/portal/src/features/checkout/hooks/useCheckout.ts` ```typescript // ❌ VIOLATION: Business logic types in UI layer type CheckoutItemType = "plan" | "installation" | "addon" | "activation" | "vpn"; interface CheckoutItem extends CatalogProductBase { quantity: number; itemType: CheckoutItemType; autoAdded?: boolean; } interface CheckoutTotals { monthlyTotal: number; oneTimeTotal: number; } ``` **Issue**: Business domain types defined in UI hooks **Impact**: Business logic scattered across layers ### 2.3 Catalog Utility Types **Location**: `apps/portal/src/features/catalog/utils/catalog.utils.ts` ```typescript // ❌ VIOLATION: TODO comment indicates missing domain type // TODO: Define CatalogFilter type properly type CatalogFilter = { category?: string; priceMin?: number; priceMax?: number; search?: string; }; ``` **Issue**: Explicitly acknowledged missing domain type **Impact**: Business concepts not properly modeled ### 2.4 Form Extension Patterns **Location**: `apps/portal/src/features/auth/components/SignupForm/SignupForm.tsx` ```typescript // ⚠️ ACCEPTABLE: Extending domain schema with UI concerns export const signupFormSchema = signupInputSchema .extend({ confirmPassword: z.string().min(1, "Please confirm your password"), }) .refine(data => data.acceptTerms === true, { message: "You must accept the terms and conditions", path: ["acceptTerms"], }) ``` **Status**: This is actually a good pattern - extending domain schemas with UI-specific fields ## 3. Cross-Cutting Concerns ### 3.1 Environment Configuration **Location**: `apps/bff/src/core/config/env.validation.ts` ```typescript // ⚠️ ACCEPTABLE: Infrastructure configuration export const envSchema = z.object({ NODE_ENV: z.enum(["development", "test", "production"]).default("development"), // ... many environment variables }); ``` **Status**: Environment configuration is appropriately infrastructure-specific ### 3.2 Database Mapping Types **Location**: `apps/bff/src/infra/mappers/mapping.mapper.ts` ```typescript // ✅ GOOD: Infrastructure mapping with clear documentation export function mapPrismaMappingToDomain(mapping: PrismaIdMapping): UserIdMapping { // Maps Prisma entity to Domain type } ``` **Status**: Proper infrastructure-to-domain mapping ## Recommendations ### 1. Immediate Actions (High Priority) #### 1.1 Move Business Types to Domain **Action**: Move the following types to appropriate domain modules: ```typescript // Move to packages/domain/orders/ export interface OrderFulfillmentValidationResult { sfOrder: SalesforceOrderRecord; clientId: number; isAlreadyProvisioned: boolean; whmcsOrderId?: string; } // Move to packages/domain/catalog/ export interface PricingTier { name: string; price: number; billingCycle: "Monthly" | "Onetime" | "Annual"; description?: string; features?: string[]; isRecommended?: boolean; originalPrice?: number; } // Move to packages/domain/orders/ export interface CheckoutItem { // Define based on CatalogProductBase + quantity + itemType } ``` #### 1.2 Centralize Validation Schemas **Action**: Move common validation patterns to domain: ```typescript // Add to packages/domain/common/schema.ts export const salesforceAccountIdSchema = z.string().min(1, "Salesforce AccountId is required"); export const nonEmptyStringSchema = z.string().min(1, "Value cannot be empty").trim(); export const soqlFieldNameSchema = z.string().trim().regex(/^[A-Za-z0-9_.]+$/, "Invalid SOQL field name"); ``` #### 1.3 Define Missing Domain Types **Action**: Complete the TODO items and define missing types: ```typescript // Add to packages/domain/catalog/ export interface CatalogFilter { category?: string; priceMin?: number; priceMax?: number; search?: string; } // Add to packages/domain/orders/ export interface OrderCreateResponse { sfOrderId: string; status: string; message: string; } ``` ### 2. Medium Priority Actions #### 2.1 Standardize Response Types **Action**: Create standardized API response types in domain: ```typescript // Add to packages/domain/common/ export interface OrderCreateResponse { sfOrderId: string; status: string; message: string; } export const orderCreateResponseSchema = z.object({ sfOrderId: z.string(), status: z.string(), message: z.string(), }); ``` #### 2.2 Refactor Checkout Logic **Action**: Move checkout business logic to domain: ```typescript // Add to packages/domain/orders/ export interface CheckoutCart { items: CheckoutItem[]; totals: CheckoutTotals; configuration: OrderConfigurations; } export function calculateCheckoutTotals(items: CheckoutItem[]): CheckoutTotals { // Move calculation logic from Portal to Domain } ``` ### 3. Long-term Improvements #### 3.1 Domain Service Layer **Action**: Consider creating domain services for complex business logic: ```typescript // Add to packages/domain/orders/services/ export class OrderValidationService { static validateFulfillmentRequest( sfOrderId: string, idempotencyKey: string ): Promise { // Move validation logic from BFF to Domain } } ``` #### 3.2 Type Safety Improvements **Action**: Enhance type safety across layers: ```typescript // Ensure all domain types are properly exported // Add runtime validation for all domain types // Implement proper error handling with domain error types ``` ## Implementation Plan ### Phase 1: Critical Violations (Week 1-2) 1. Move `OrderFulfillmentValidationResult` to domain 2. Move `PricingTier` to domain 3. Centralize validation schemas 4. Define missing `CatalogFilter` type ### Phase 2: Business Logic Consolidation (Week 3-4) 1. Move checkout types to domain 2. Standardize API response types 3. Refactor validation services 4. Update imports across applications ### Phase 3: Architecture Improvements (Week 5-6) 1. Create domain service layer 2. Implement proper error handling 3. Add comprehensive type exports 4. Update documentation ## Success Metrics - **Type Reuse**: 90%+ of business types defined in domain - **Validation Consistency**: Single validation schema per business rule - **Import Clarity**: Clear domain imports in BFF and Portal - **Documentation**: Updated architecture documentation ## Conclusion The analysis reveals that while the domain package is well-structured, there are several violations where business logic and types are scattered across the BFF and Portal layers. The recommended actions will help establish the domain package as the true single source of truth, improving maintainability and consistency across the application. The violations are primarily in business logic types and validation schemas that should be centralized in the domain layer. The form extension patterns in the Portal are actually good examples of how to properly extend domain schemas with UI-specific concerns. --- **Report Generated**: $(date) **Analysis Scope**: BFF and Portal applications **Domain Package**: `packages/domain/`