Assist_Design/docs/_archive/customer-domain/CUSTOMER-DOMAIN-NAMING.md

7.0 KiB

Customer Domain - Type Naming Clarity

The Problem

Current naming is confusing:

  • PortalUser - What is this?
  • CustomerProfile - Is this different from a user?
  • UserProfile - How is this different from the above?
  • AuthenticatedUser - Another alias?

Proposed Clear Naming

Core Principle: Same Entity, Different Data Sources

A customer/user is ONE business entity with data from TWO sources:

  1. Portal Database → Authentication & account state
  2. WHMCS → Profile & billing information
// packages/domain/customer/types.ts

// ============================================================================
// User Auth State (from Portal Database)
// ============================================================================

/**
 * User authentication and account state stored in portal database
 * Source: Portal DB (Prisma)
 * Contains: Auth-related fields only
 */
export const userAuthSchema = z.object({
  id: z.string().uuid(),              // Portal user ID (primary key)
  email: z.string().email(),          // Email (for login)
  role: z.enum(["USER", "ADMIN"]),    // User role
  emailVerified: z.boolean(),         // Email verification status
  mfaEnabled: z.boolean(),            // MFA enabled flag
  lastLoginAt: z.string().optional(), // Last login timestamp
  createdAt: z.string(),              // Account created date
  updatedAt: z.string(),              // Account updated date
});

export type UserAuth = z.infer<typeof userAuthSchema>;


// ============================================================================
// Customer Profile Data (from WHMCS)
// ============================================================================

/**
 * Customer profile and billing information from WHMCS
 * Source: WHMCS API
 * Contains: Personal info, address, preferences
 */
export const customerDataSchema = z.object({
  whmcsClientId: z.number().int(),           // WHMCS client ID
  firstname: z.string().nullable().optional(),
  lastname: z.string().nullable().optional(),
  fullname: z.string().nullable().optional(),
  companyname: z.string().nullable().optional(),
  phonenumber: z.string().nullable().optional(),
  address: customerAddressSchema.optional(),
  language: z.string().nullable().optional(),
  currencyCode: z.string().nullable().optional(),
  // ... other WHMCS fields
});

export type CustomerData = z.infer<typeof customerDataSchema>;


// ============================================================================
// Complete User (Auth + Profile Data)
// ============================================================================

/**
 * Complete user profile combining auth state and customer data
 * Composition: UserAuth (portal) + CustomerData (WHMCS)
 * This is what gets returned in API responses
 */
export const userSchema = userAuthSchema.extend({
  // Add customer data fields
  firstname: z.string().nullable().optional(),
  lastname: z.string().nullable().optional(),
  fullname: z.string().nullable().optional(),
  companyname: z.string().nullable().optional(),
  phonenumber: z.string().nullable().optional(),
  address: customerAddressSchema.optional(),
  language: z.string().nullable().optional(),
  currencyCode: z.string().nullable().optional(),
});

/**
 * Complete user - exported as 'User' for simplicity
 * Use this in API responses and business logic
 */
export type User = z.infer<typeof userSchema>;

Naming Comparison

UserAuth          // Auth state from portal DB
CustomerData      // Profile data from WHMCS  
User              // Complete entity

Pros:

  • Clear distinction: "Auth" = authentication, "Data" = profile information
  • Simple exports: User is the main type
  • Intuitive: "I need UserAuth for JWT" vs "I need User for profile API"

Cons:

  • "CustomerData" might seem weird (but it's accurate - it's WHMCS customer data)

Option B: Portal/Customer (Current-ish)

PortalUser        // Auth state from portal DB
CustomerProfile   // Profile data from WHMCS
User              // Complete entity

Pros:

  • "Portal" clearly indicates source
  • "CustomerProfile" is business-domain language

Cons:

  • "PortalUser" vs "User" - what's the difference?
  • Implies there are different "user" entities (there's only ONE user)

Option C: Auth/Profile (Simple)

AuthUser          // Auth state
ProfileData       // Profile info
User              // Complete entity

Pros:

  • Very simple and clear
  • Purpose-driven naming

Cons:

  • Less obvious where data comes from

Option D: Keep Descriptive (Verbose but Clear)

UserAuthState           // Auth state from portal DB
CustomerProfileData     // Profile data from WHMCS
UserWithProfile         // Complete entity

Pros:

  • Extremely explicit
  • No ambiguity

Cons:

  • Verbose
  • "UserWithProfile" is awkward
// Clear and intuitive
import type { UserAuth, CustomerData, User } from "@customer-portal/domain/customer";

// In JWT strategy - only need auth state
async validate(payload: JwtPayload): Promise<UserAuth> {
  return this.getAuthState(payload.sub);
}

// In profile API - need complete user
async getProfile(userId: string): Promise<User> {
  const auth = await this.getAuthState(userId);
  const data = await this.getCustomerData(userId);
  
  return {
    ...auth,
    ...data,
  };
}

Provider Structure

packages/domain/customer/providers/
├── portal/
│   ├── types.ts           # PrismaUserRaw interface
│   └── mapper.ts          # Prisma → UserAuth
│
└── whmcs/
    ├── types.ts           # WhmcsClientRaw interface
    └── mapper.ts          # WHMCS → CustomerData

What Gets Exported

// packages/domain/customer/index.ts

export type {
  // Core types (use these in your code)
  User,              // Complete user (UserAuth + CustomerData)
  UserAuth,          // Auth state only (for JWT, auth checks)
  CustomerData,      // Profile data only (rarely used directly)
  
  // Supporting types
  CustomerAddress,
  // ... etc
} from "./types";

export {
  // Schemas for validation
  userSchema,
  userAuthSchema,
  customerDataSchema,
  // ... etc
} from "./types";

export * as Providers from "./providers";

Auth Domain References User

// packages/domain/auth/types.ts

import { userSchema } from "../customer/types";

export const authResponseSchema = z.object({
  user: userSchema,        // Reference the User schema from customer domain
  tokens: authTokensSchema,
});

export type AuthResponse = z.infer<typeof authResponseSchema>;

Summary

Entity: ONE user/customer entity

Data Sources:

  • UserAuth - From portal database (auth state)
  • CustomerData - From WHMCS (profile data)

Combined:

  • User - Complete entity (auth + profile)

Usage:

  • JWT validation → UserAuth
  • API responses → User
  • Internal mapping → CustomerData (via providers)