Assist_Design/DOMAIN_VIOLATIONS_REPORT.md
barsa e5ce4e166c Refactor mappers and services for improved type safety and code clarity
- Updated export statements in user and mapping mappers for consistency.
- Enhanced FreebitAuthService to explicitly define response types for better type inference.
- Refactored various services to improve error handling and response structure.
- Cleaned up unused code and comments across multiple files to enhance readability.
- Improved type annotations in invoice and subscription services for better validation and consistency.
2025-10-22 10:58:16 +09:00

11 KiB

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

// ❌ 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

// ❌ 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

// ❌ 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

// ❌ 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

// ❌ 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

// ❌ 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

// ❌ 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

// ❌ 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

// ⚠️ 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

// ⚠️ 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

// ✅ 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:

// 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:

// 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:

// 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:

// 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:

// 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:

// Add to packages/domain/orders/services/
export class OrderValidationService {
  static validateFulfillmentRequest(
    sfOrderId: string,
    idempotencyKey: string
  ): Promise<OrderFulfillmentValidationResult> {
    // Move validation logic from BFF to Domain
  }
}

3.2 Type Safety Improvements

Action: Enhance type safety across layers:

// 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/