From 0c63bc5c33e56c3dd320ecd827b7d3f96ee0212b Mon Sep 17 00:00:00 2001 From: barsa Date: Tue, 24 Feb 2026 11:57:43 +0900 Subject: [PATCH] refactor: domain package cleanup - Remove validation wrapper functions from common/validation.ts (use Zod schemas directly) - Delete duplicate CheckoutItem/CheckoutTotals/CheckoutCart/OrderCreateResponse from orders/contract.ts - Delete empty orders/checkout.ts - Remove unused MIGRATION_STEPS/MIGRATION_TRANSFER_ITEMS UI constants from auth/forms.ts - Standardize checkout/contract.ts to not re-export schema types - Fix customer/providers/index.ts to not re-export contract types through providers barrel --- apps/bff/src/core/utils/validation.util.ts | 9 +-- .../services/salesforce-account.service.ts | 2 +- .../users/infra/user-auth.repository.ts | 8 +- packages/domain/auth/forms.ts | 18 ----- packages/domain/auth/index.ts | 2 - packages/domain/checkout/contract.ts | 4 +- packages/domain/common/validation.ts | 50 +------------ packages/domain/customer/index.ts | 8 +- packages/domain/customer/providers/index.ts | 7 +- packages/domain/orders/checkout.ts | 12 --- packages/domain/orders/contract.ts | 73 +------------------ packages/domain/orders/helpers.ts | 2 +- packages/domain/orders/index.ts | 5 -- .../orders/providers/salesforce/mapper.ts | 2 +- .../domain/orders/providers/whmcs/mapper.ts | 2 +- packages/domain/orders/schema.ts | 4 + packages/domain/orders/utils.ts | 3 +- 17 files changed, 33 insertions(+), 178 deletions(-) delete mode 100644 packages/domain/orders/checkout.ts diff --git a/apps/bff/src/core/utils/validation.util.ts b/apps/bff/src/core/utils/validation.util.ts index 275aec66..d1d8a3a0 100644 --- a/apps/bff/src/core/utils/validation.util.ts +++ b/apps/bff/src/core/utils/validation.util.ts @@ -1,11 +1,10 @@ import { BadRequestException } from "@nestjs/common"; -import { validateUuidV4OrThrow } from "@customer-portal/domain/common"; +import { uuidSchema } from "@customer-portal/domain/common"; export function parseUuidOrThrow(id: string, message = "Invalid ID format"): string { - try { - // Domain validator throws its own Error; we translate to a BadRequestException with our message. - return validateUuidV4OrThrow(id); - } catch { + const result = uuidSchema.safeParse(id); + if (!result.success) { throw new BadRequestException(message); } + return result.data; } diff --git a/apps/bff/src/integrations/salesforce/services/salesforce-account.service.ts b/apps/bff/src/integrations/salesforce/services/salesforce-account.service.ts index a0b03b90..c3f13921 100644 --- a/apps/bff/src/integrations/salesforce/services/salesforce-account.service.ts +++ b/apps/bff/src/integrations/salesforce/services/salesforce-account.service.ts @@ -3,7 +3,7 @@ import { Logger } from "nestjs-pino"; import { ConfigService } from "@nestjs/config"; import { extractErrorMessage } from "@bff/core/utils/error.util.js"; import { SalesforceConnection } from "./salesforce-connection.service.js"; -import type { SalesforceAccountRecord } from "@customer-portal/domain/customer/providers"; +import type { SalesforceAccountRecord } from "@customer-portal/domain/customer"; import type { SalesforceResponse } from "@customer-portal/domain/common/providers"; import { customerNumberSchema, salesforceIdSchema } from "@customer-portal/domain/common"; diff --git a/apps/bff/src/modules/users/infra/user-auth.repository.ts b/apps/bff/src/modules/users/infra/user-auth.repository.ts index e2ceeeb5..019e67f8 100644 --- a/apps/bff/src/modules/users/infra/user-auth.repository.ts +++ b/apps/bff/src/modules/users/infra/user-auth.repository.ts @@ -1,7 +1,7 @@ import { Injectable, BadRequestException } from "@nestjs/common"; import type { User as PrismaUser } from "@prisma/client"; import { PrismaService } from "@bff/infra/database/prisma.service.js"; -import { normalizeAndValidateEmail } from "@customer-portal/domain/common"; +import { emailSchema } from "@customer-portal/domain/common"; import { extractErrorMessage } from "@bff/core/utils/error.util.js"; import { parseUuidOrThrow } from "@bff/core/utils/validation.util.js"; @@ -17,7 +17,7 @@ export class UserAuthRepository { constructor(private readonly prisma: PrismaService) {} async findByEmail(email: string): Promise { - const normalized = normalizeAndValidateEmail(email); + const normalized = emailSchema.parse(email); try { return await this.prisma.user.findUnique({ where: { email: normalized } }); } catch (error) { @@ -41,7 +41,7 @@ export class UserAuthRepository { throw new BadRequestException("Email is required to create a user"); } - const normalizedEmail = normalizeAndValidateEmail(data.email); + const normalizedEmail = emailSchema.parse(data.email); try { return await this.prisma.user.create({ @@ -72,7 +72,7 @@ export class UserAuthRepository { async updateEmail(id: string, email: string): Promise { const validId = parseUuidOrThrow(id, INVALID_USER_ID_FORMAT); - const normalized = normalizeAndValidateEmail(email); + const normalized = emailSchema.parse(email); try { await this.prisma.user.update({ where: { id: validId }, diff --git a/packages/domain/auth/forms.ts b/packages/domain/auth/forms.ts index 55bf2ee7..eeac6e8f 100644 --- a/packages/domain/auth/forms.ts +++ b/packages/domain/auth/forms.ts @@ -56,21 +56,3 @@ export function getPasswordStrengthDisplay(strength: number): { if (strength >= 60) return { label: "Fair", colorClass: "bg-yellow-500" }; return { label: "Weak", colorClass: "bg-red-500" }; } - -// ============================================================================ -// Migration Info (Business Constants) -// ============================================================================ - -export const MIGRATION_TRANSFER_ITEMS = [ - "All active services", - "Billing history", - "Support cases", - "Account details", -] as const; - -export const MIGRATION_STEPS = [ - "Enter your legacy portal email and password", - "We verify your account and migrate your data", - "Create a new secure password for the upgraded portal", - "Access your dashboard with all your services ready", -] as const; diff --git a/packages/domain/auth/index.ts b/packages/domain/auth/index.ts index df42aec5..61e6b2fc 100644 --- a/packages/domain/auth/index.ts +++ b/packages/domain/auth/index.ts @@ -107,8 +107,6 @@ export { PASSWORD_REQUIREMENTS, checkPasswordStrength, getPasswordStrengthDisplay, - MIGRATION_TRANSFER_ITEMS, - MIGRATION_STEPS, type PasswordRequirementKey, } from "./forms.js"; diff --git a/packages/domain/checkout/contract.ts b/packages/domain/checkout/contract.ts index 3d4c743a..f54199ad 100644 --- a/packages/domain/checkout/contract.ts +++ b/packages/domain/checkout/contract.ts @@ -19,5 +19,5 @@ export const CHECKOUT_ORDER_TYPE = { export type CheckoutOrderTypeValue = (typeof CHECKOUT_ORDER_TYPE)[keyof typeof CHECKOUT_ORDER_TYPE]; -// Re-export types from schema -export type { OrderType, PriceBreakdownItem, CartItem } from "./schema.js"; +// Schema-derived types (OrderType, PriceBreakdownItem, CartItem) +// are exported from index.ts, not contract.ts. diff --git a/packages/domain/common/validation.ts b/packages/domain/common/validation.ts index ef80a8f4..c89305be 100644 --- a/packages/domain/common/validation.ts +++ b/packages/domain/common/validation.ts @@ -1,8 +1,8 @@ /** * Common Domain - Validation Utilities * - * Generic validation functions used across all domains. - * These are pure functions with no infrastructure dependencies. + * Generic validation schemas used across all domains. + * These are pure schemas with no infrastructure dependencies. */ import { z } from "zod"; @@ -23,49 +23,3 @@ export const requiredStringSchema = z.string().min(1, "This field is required"). * Generic schema for customer/account identifiers */ export const customerNumberSchema = z.string().min(1, "Customer number is required").trim(); - -/** - * Normalize and validate an email address - * - * This is a convenience wrapper that throws on invalid input. - * For validation without throwing, use the emailSchema directly with .safeParse() - * - * @throws Error if email format is invalid - */ -export function normalizeAndValidateEmail(email: string): string { - const emailValidationSchema = z - .string() - .email() - .transform(e => e.toLowerCase().trim()); - return emailValidationSchema.parse(email); -} - -/** - * Validate a UUID (v4) - * - * This is a convenience wrapper that throws on invalid input. - * For validation without throwing, use the uuidSchema directly with .safeParse() - * - * @throws Error if UUID format is invalid - */ -export function validateUuidV4OrThrow(id: string, message = "Invalid UUID format"): string { - const parsed = uuidSchema.safeParse(id); - if (!parsed.success) { - throw new Error(message); - } - return parsed.data; -} - -/** - * Check if a string is a valid email (non-throwing) - */ -export function isValidEmail(email: string): boolean { - return z.string().email().safeParse(email).success; -} - -/** - * Check if a string is a valid UUID (non-throwing) - */ -export function isValidUuid(id: string): boolean { - return uuidSchema.safeParse(id).success; -} diff --git a/packages/domain/customer/index.ts b/packages/domain/customer/index.ts index 5f68e1ff..9261f251 100644 --- a/packages/domain/customer/index.ts +++ b/packages/domain/customer/index.ts @@ -15,7 +15,13 @@ // Constants // ============================================================================ -export { USER_ROLE, type UserRoleValue } from "./contract.js"; +export { + USER_ROLE, + type UserRoleValue, + type SalesforceAccountFieldMap, + type SalesforceAccountRecord, + type SalesforceContactRecord, +} from "./contract.js"; // ============================================================================ // Domain Types (Clean Names - Public API) diff --git a/packages/domain/customer/providers/index.ts b/packages/domain/customer/providers/index.ts index ed015f22..569243b3 100644 --- a/packages/domain/customer/providers/index.ts +++ b/packages/domain/customer/providers/index.ts @@ -9,12 +9,7 @@ export * from "./portal/index.js"; export * from "./whmcs/index.js"; -// Provider-specific integration types (BFF-only convenience re-exports) -export type { - SalesforceAccountFieldMap, - SalesforceAccountRecord, - SalesforceContactRecord, -} from "../contract.js"; +// Provider-specific WHMCS integration types (BFF-only) export type { WhmcsAddClientParams, WhmcsValidateLoginParams, diff --git a/packages/domain/orders/checkout.ts b/packages/domain/orders/checkout.ts deleted file mode 100644 index b2477c13..00000000 --- a/packages/domain/orders/checkout.ts +++ /dev/null @@ -1,12 +0,0 @@ -/** - * Orders Domain - Checkout Types - * - * Minimal type definitions for checkout flow. - * Frontend handles its own URL param serialization. - */ - -// This file is intentionally minimal after cleanup. -// The build/derive/normalize functions were removed as they were -// unnecessary abstractions that should be handled by the frontend. -// -// See CLEANUP_PROPOSAL_NORMALIZERS.md for details. diff --git a/packages/domain/orders/contract.ts b/packages/domain/orders/contract.ts index c7eb3a15..f588d7ee 100644 --- a/packages/domain/orders/contract.ts +++ b/packages/domain/orders/contract.ts @@ -5,8 +5,6 @@ * Validated types are derived from schemas (see schema.ts). */ -import type { OrderConfigurations } from "./schema.js"; - // ============================================================================ // Order Type Constants // ============================================================================ @@ -132,74 +130,9 @@ export interface UserMapping { sfAccountId?: string | null | undefined; } -// ============================================================================ -// Checkout Types -// ============================================================================ - -/** - * Individual item in checkout cart - */ -export interface CheckoutItem { - id: string; - sku: string; - name: string; - description?: string | undefined; - monthlyPrice?: number | undefined; - oneTimePrice?: number | undefined; - quantity: number; - itemType: "plan" | "installation" | "addon" | "activation" | "vpn"; - autoAdded?: boolean | undefined; -} - -/** - * Checkout totals calculation - */ -export interface CheckoutTotals { - monthlyTotal: number; - oneTimeTotal: number; -} - -/** - * Complete checkout cart with items, totals, and configuration - */ -export interface CheckoutCart { - items: CheckoutItem[]; - totals: CheckoutTotals; - configuration: OrderConfigurations; -} - -/** - * Order creation response from BFF - */ -export interface OrderCreateResponse { - sfOrderId: string; - status: string; - message: string; -} - -/** - * Order fulfillment validation result - */ // NOTE: Provider-specific fulfillment types are intentionally not part of the public orders contract. // BFF integration code should import them from: // @customer-portal/domain/orders/providers - -// ============================================================================ -// Re-export Types from Schema (Schema-First Approach) -// ============================================================================ - -export type { - // Order item types - OrderItemSummary, - OrderItemDetails, - // Order types - OrderSummary, - OrderDetails, - // Query and creation types - OrderQueryParams, - OrderConfigurationsAddress, - OrderConfigurations, - CreateOrderRequest, - OrderBusinessValidation, - SfOrderIdParam, -} from "./schema.js"; +// +// Checkout types (CheckoutItem, CheckoutTotals, CheckoutCart, OrderCreateResponse) +// and other schema-derived types are exported from schema.ts directly. diff --git a/packages/domain/orders/helpers.ts b/packages/domain/orders/helpers.ts index 89819795..92756a5f 100644 --- a/packages/domain/orders/helpers.ts +++ b/packages/domain/orders/helpers.ts @@ -1,6 +1,7 @@ import { orderConfigurationsSchema, orderSelectionsSchema, + type CheckoutTotals, type OrderConfigurations, type OrderSelections, type OrderItemSummary, @@ -10,7 +11,6 @@ import { type OrderDisplayItemChargeKind, } from "./schema.js"; import { ORDER_TYPE } from "./contract.js"; -import type { CheckoutTotals } from "./contract.js"; import type { SimConfigureFormData } from "../sim/index.js"; import type { WhmcsOrderItem } from "./providers/whmcs/raw.types.js"; diff --git a/packages/domain/orders/index.ts b/packages/domain/orders/index.ts index 113bb760..d7a23383 100644 --- a/packages/domain/orders/index.ts +++ b/packages/domain/orders/index.ts @@ -15,11 +15,6 @@ export { type OrderType, type OrderTypeValue, type UserMapping, - // Checkout types - type CheckoutItem, - type CheckoutTotals, - type CheckoutCart, - type OrderCreateResponse, // Constants ORDER_TYPE, ORDER_STATUS, diff --git a/packages/domain/orders/providers/salesforce/mapper.ts b/packages/domain/orders/providers/salesforce/mapper.ts index fca1e16b..c101abbd 100644 --- a/packages/domain/orders/providers/salesforce/mapper.ts +++ b/packages/domain/orders/providers/salesforce/mapper.ts @@ -9,7 +9,7 @@ import type { OrderItemDetails, OrderItemSummary, OrderSummary, -} from "../../contract.js"; +} from "../../schema.js"; import { normalizeBillingCycle } from "../../helpers.js"; import { orderDetailsSchema, orderSummarySchema, orderItemDetailsSchema } from "../../schema.js"; import type { diff --git a/packages/domain/orders/providers/whmcs/mapper.ts b/packages/domain/orders/providers/whmcs/mapper.ts index dc4575cf..3d987bc6 100644 --- a/packages/domain/orders/providers/whmcs/mapper.ts +++ b/packages/domain/orders/providers/whmcs/mapper.ts @@ -4,7 +4,7 @@ * Transforms normalized order data to WHMCS API format. */ -import type { OrderDetails, OrderItemDetails } from "../../contract.js"; +import type { OrderDetails, OrderItemDetails } from "../../schema.js"; import { normalizeBillingCycle } from "../../helpers.js"; import { serializeWhmcsKeyValueMap } from "../../../common/providers/whmcs-utils/index.js"; import { diff --git a/packages/domain/orders/schema.ts b/packages/domain/orders/schema.ts index c0634e03..751d92c9 100644 --- a/packages/domain/orders/schema.ts +++ b/packages/domain/orders/schema.ts @@ -387,6 +387,10 @@ export type OrderConfigurationsAddress = z.infer; export type CreateOrderRequest = z.infer; export type OrderBusinessValidation = z.infer; +export type CheckoutItem = z.infer; +export type CheckoutTotals = z.infer; +export type CheckoutCart = z.infer; +export type OrderCreateResponse = z.infer; export type CheckoutBuildCartRequest = z.infer; export type CheckoutBuildCartResponse = z.infer; export type CheckoutSessionCreateOrderRequest = z.infer< diff --git a/packages/domain/orders/utils.ts b/packages/domain/orders/utils.ts index 9ed2bff7..6d5c7baf 100644 --- a/packages/domain/orders/utils.ts +++ b/packages/domain/orders/utils.ts @@ -1,11 +1,12 @@ import { orderConfigurationsSchema, orderSelectionsSchema, + type CheckoutCart, type OrderConfigurations, type CreateOrderRequest, type OrderSelections, } from "./schema.js"; -import { ORDER_TYPE, type CheckoutCart, type OrderTypeValue } from "./contract.js"; +import { ORDER_TYPE, type OrderTypeValue } from "./contract.js"; export type CheckoutOrderTypeValue = Extract;