# Customer Domain Refactor Plan
## Moving User Entity Types from Auth to Customer Domain

## Overview

**Problem**: Auth domain currently contains both authentication mechanisms AND user entity types, which mixes concerns.

**Solution**: 
- Move all user entity types (`PortalUser`, `UserProfile`) from `auth/` to `customer/` domain
- Add Portal (Prisma) as a provider alongside WHMCS
- Keep auth domain focused ONLY on authentication mechanisms
- Consolidate schemas and contracts into single files where appropriate

## Architecture Vision

```
packages/domain/customer/
├── index.ts                    # Main exports
├── types.ts                    # ALL types in one file (schemas + inferred types)
│   ├── Schemas (Zod)
│   │   ├── customerAddressSchema
│   │   ├── portalUserSchema         (NEW - from auth)
│   │   ├── customerProfileSchema
│   │   └── userProfileSchema        (NEW - from auth)
│   │
│   └── Types (inferred from schemas)
│       ├── CustomerAddress
│       ├── PortalUser              (NEW - auth state from portal DB)
│       ├── CustomerProfile         (WHMCS profile data)
│       └── UserProfile             (NEW - PortalUser + CustomerProfile)
│
├── providers/
│   ├── index.ts
│   │
│   ├── portal/                     (NEW - Portal DB provider)
│   │   ├── index.ts
│   │   ├── types.ts                # PrismaUserRaw
│   │   └── mapper.ts               # Prisma → PortalUser
│   │
│   ├── whmcs/                      (EXISTING)
│   │   ├── index.ts
│   │   ├── types.ts                # WHMCS raw types
│   │   └── mapper.ts               # WHMCS → CustomerProfile
│   │
│   └── salesforce/                 (EXISTING if needed)
│

packages/domain/auth/
├── index.ts
├── types.ts                        # Authentication mechanism types ONLY
│   ├── Schemas (Zod)
│   │   ├── loginRequestSchema
│   │   ├── signupRequestSchema
│   │   ├── passwordResetSchema
│   │   ├── authTokensSchema
│   │   ├── authResponseSchema      (user: UserProfile reference, tokens)
│   │   └── mfaSchemas
│   │
│   └── Types (inferred)
│       ├── LoginRequest
│       ├── SignupRequest
│       ├── AuthTokens
│       └── AuthResponse
│
└── NO user entity types, NO PortalUser, NO UserProfile
```

## Detailed Implementation Plan

### Phase 1: Create New Customer Domain Structure

#### 1.1 Create consolidated types file
**File:** `packages/domain/customer/types.ts`

Move from multiple files into one:
- From `customer/schema.ts`: All existing customer schemas
- From `customer/contract.ts`: Interface definitions (convert to schema-first)
- From `auth/schema.ts`: `portalUserSchema`, `userProfileSchema`

Structure:
```typescript
import { z } from "zod";

// ============================================================================
// Common Schemas
// ============================================================================
export const customerAddressSchema = z.object({...});
export const customerEmailPreferencesSchema = z.object({...});
// ... other existing schemas

// ============================================================================
// Portal User Schema (Auth State from Portal DB)
// ============================================================================
/**
 * PortalUser represents ONLY auth state stored in portal database
 * Provider: Prisma (portal DB)
 */
export const portalUserSchema = z.object({
  id: z.string().uuid(),
  email: z.string().email(),
  role: z.enum(["USER", "ADMIN"]),
  emailVerified: z.boolean(),
  mfaEnabled: z.boolean(),
  lastLoginAt: z.string().optional(),
  createdAt: z.string(),
  updatedAt: z.string(),
});

// ============================================================================
// Customer Profile Schema (WHMCS Profile Data)
// ============================================================================
/**
 * CustomerProfile represents profile data from WHMCS
 * Provider: WHMCS
 */
export const customerProfileSchema = z.object({
  id: z.string(),
  email: z.string(),
  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(),
  createdAt: z.string().optional(),
  updatedAt: z.string().optional(),
});

// ============================================================================
// User Profile Schema (Complete User = PortalUser + CustomerProfile)
// ============================================================================
/**
 * UserProfile is the complete authenticated user
 * Composition: PortalUser (auth state) + CustomerProfile (WHMCS data)
 */
export const userProfileSchema = portalUserSchema.extend({
  // Add CustomerProfile 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(),
});

// ============================================================================
// Inferred Types (Schema-First)
// ============================================================================
export type CustomerAddress = z.infer<typeof customerAddressSchema>;
export type PortalUser = z.infer<typeof portalUserSchema>;
export type CustomerProfile = z.infer<typeof customerProfileSchema>;
export type UserProfile = z.infer<typeof userProfileSchema>;
// ... all other types
```

#### 1.2 Create Portal Provider
**Directory:** `packages/domain/customer/providers/portal/`

**File:** `packages/domain/customer/providers/portal/types.ts`
```typescript
import type { UserRole } from "../../types";

/**
 * Raw Prisma user data
 * This interface matches Prisma schema but doesn't depend on @prisma/client
 */
export interface PrismaUserRaw {
  id: string;
  email: string;
  passwordHash: string | null;
  role: UserRole;
  mfaSecret: string | null;
  emailVerified: boolean;
  failedLoginAttempts: number;
  lockedUntil: Date | null;
  lastLoginAt: Date | null;
  createdAt: Date;
  updatedAt: Date;
}
```

**File:** `packages/domain/customer/providers/portal/mapper.ts`
```typescript
import type { PrismaUserRaw } from "./types";
import type { PortalUser } from "../../types";

/**
 * Maps raw Prisma user data to PortalUser domain type
 */
export function mapPrismaUserToPortalUser(raw: PrismaUserRaw): PortalUser {
  return {
    id: raw.id,
    email: raw.email,
    role: raw.role,
    mfaEnabled: !!raw.mfaSecret,
    emailVerified: raw.emailVerified,
    lastLoginAt: raw.lastLoginAt?.toISOString(),
    createdAt: raw.createdAt.toISOString(),
    updatedAt: raw.updatedAt.toISOString(),
  };
}
```

**File:** `packages/domain/customer/providers/portal/index.ts`
```typescript
export * from "./mapper";
export * from "./types";
```


#### 1.3 Update Customer Domain Index
**File:** `packages/domain/customer/index.ts`
```typescript
/**
 * Customer Domain
 * 
 * Contains all user/customer entity types and their providers:
 * - PortalUser: Auth state from portal DB (via Prisma)
 * - CustomerProfile: Profile data from WHMCS
 * - UserProfile: Complete user (PortalUser + CustomerProfile)
 */

// Export all types
export type {
  // Core types
  PortalUser,
  CustomerProfile,
  UserProfile,
  CustomerAddress,
  // ... all other types
} from "./types";

// Export schemas for validation
export {
  portalUserSchema,
  customerProfileSchema,
  userProfileSchema,
  customerAddressSchema,
  // ... all other schemas
} from "./types";

// Export providers
export * as Providers from "./providers";
```

#### 1.4 Update Providers Index
**File:** `packages/domain/customer/providers/index.ts`
```typescript
import * as PortalModule from "./portal";
import * as WhmcsModule from "./whmcs";

export const Portal = PortalModule;
export const Whmcs = WhmcsModule;

export { PortalModule, WhmcsModule };
```

### Phase 2: Clean Up Auth Domain

#### 2.1 Remove user entity types from auth
**File:** `packages/domain/auth/types.ts` (create new, consolidate schema.ts + contract.ts)

Remove:
- `portalUserSchema` → moved to customer domain
- `userProfileSchema` → moved to customer domain
- `PortalUser` type → moved to customer domain
- `UserProfile` / `AuthenticatedUser` type → moved to customer domain

Keep ONLY:
- Authentication mechanism schemas (login, signup, password reset, MFA)
- Token schemas (authTokensSchema, refreshTokenSchema)
- Auth response schemas (authResponseSchema references UserProfile from customer domain)

Structure:
```typescript
import { z } from "zod";
import { userProfileSchema } from "../customer/types";

// ============================================================================
// Request Schemas (Authentication Mechanisms)
// ============================================================================
export const loginRequestSchema = z.object({...});
export const signupRequestSchema = z.object({...});
export const passwordResetRequestSchema = z.object({...});
// ... all auth request schemas

// ============================================================================
// Token Schemas
// ============================================================================
export const authTokensSchema = z.object({
  accessToken: z.string(),
  refreshToken: z.string(),
  expiresAt: z.string(),
  refreshExpiresAt: z.string(),
  tokenType: z.literal("Bearer"),
});

// ============================================================================
// Response Schemas (Reference UserProfile from Customer Domain)
// ============================================================================
export const authResponseSchema = z.object({
  user: userProfileSchema, // from customer domain
  tokens: authTokensSchema,
});

export const signupResultSchema = z.object({
  user: userProfileSchema,
  tokens: authTokensSchema,
});

// ============================================================================
// Inferred Types
// ============================================================================
export type LoginRequest = z.infer<typeof loginRequestSchema>;
export type AuthTokens = z.infer<typeof authTokensSchema>;
export type AuthResponse = z.infer<typeof authResponseSchema>;
export type SignupResult = z.infer<typeof signupResultSchema>;
// ... all other auth types
```

#### 2.2 Delete auth/schema.ts and auth/contract.ts
After consolidating into `auth/types.ts`, remove:
- `packages/domain/auth/schema.ts`
- `packages/domain/auth/contract.ts`

#### 2.3 Delete auth/providers/
Remove `packages/domain/auth/providers/` entirely (Portal provider moved to customer domain)

#### 2.4 Update Auth Domain Index
**File:** `packages/domain/auth/index.ts`
```typescript
/**
 * Auth Domain
 * 
 * Contains ONLY authentication mechanisms:
 * - Login, Signup, Password Management
 * - Token Management (JWT)
 * - MFA, SSO
 * 
 * User entity types are in customer domain.
 */

export type {
  LoginRequest,
  SignupRequest,
  AuthTokens,
  AuthResponse,
  SignupResult,
  PasswordChangeResult,
  // ... all auth mechanism types
} from "./types";

export {
  loginRequestSchema,
  signupRequestSchema,
  authTokensSchema,
  authResponseSchema,
  // ... all auth schemas
} from "./types";
```

### Phase 3: Update BFF Layer

#### 3.1 Update BFF User Mapper
**File:** `apps/bff/src/infra/mappers/user.mapper.ts`

```typescript
import type { User as PrismaUser } from "@prisma/client";
import { Providers as CustomerProviders } from "@customer-portal/domain/customer";
import type { PortalUser } from "@customer-portal/domain/customer";

/**
 * Adapter: Converts Prisma User to domain PortalUser
 */
export function mapPrismaUserToDomain(user: PrismaUser): PortalUser {
  const prismaUserRaw: CustomerProviders.Portal.PrismaUserRaw = {
    id: user.id,
    email: user.email,
    passwordHash: user.passwordHash,
    role: user.role,
    mfaSecret: user.mfaSecret,
    emailVerified: user.emailVerified,
    failedLoginAttempts: user.failedLoginAttempts,
    lockedUntil: user.lockedUntil,
    lastLoginAt: user.lastLoginAt,
    createdAt: user.createdAt,
    updatedAt: user.updatedAt,
  };
  
  return CustomerProviders.Portal.mapPrismaUserToPortalUser(prismaUserRaw);
}
```

#### 3.2 Update Auth Workflow Services
Update imports in:
- `apps/bff/src/modules/auth/infra/workflows/workflows/signup-workflow.service.ts`
- `apps/bff/src/modules/auth/infra/workflows/workflows/password-workflow.service.ts`
- `apps/bff/src/modules/auth/application/auth.facade.ts`

Change:
```typescript
// OLD
import type { UserProfile, SignupResult } from "@customer-portal/domain/auth";

// NEW
import type { UserProfile } from "@customer-portal/domain/customer";
import type { SignupResult } from "@customer-portal/domain/auth";
```

#### 3.3 Update Users Service
**File:** `apps/bff/src/modules/users/users.service.ts`

Construct UserProfile directly (already doing this!):
```typescript
import { 
  type PortalUser, 
  type CustomerProfile, 
  type UserProfile,
} from "@customer-portal/domain/customer";

async getProfile(userId: string): Promise<UserProfile> {
  const user = await this.prisma.user.findUnique({ where: { id: userId } });
  const client = await this.whmcsService.getClientDetails(mapping.whmcsClientId);
  
  // Construct UserProfile directly
  const profile: UserProfile = {
    // Auth state from portal
    id: user.id,
    email: client.email,
    role: user.role,
    emailVerified: user.emailVerified,
    mfaEnabled: user.mfaSecret !== null,
    lastLoginAt: user.lastLoginAt?.toISOString(),
    createdAt: user.createdAt.toISOString(),
    updatedAt: user.updatedAt.toISOString(),
    
    // Profile from WHMCS
    firstname: client.firstname || null,
    lastname: client.lastname || null,
    fullname: client.fullname || null,
    companyname: client.companyName || null,
    phonenumber: client.phoneNumber || null,
    address: client.address || undefined,
    language: client.language || null,
    currencyCode: client.currencyCode || null,
  };
  
  return profile;
}
```

#### 3.4 Update JWT Strategy
**File:** `apps/bff/src/modules/auth/presentation/strategies/jwt.strategy.ts`

```typescript
import type { PortalUser } from "@customer-portal/domain/customer";

async validate(payload: JwtPayload): Promise<PortalUser> {
  const user = await this.prisma.user.findUnique({...});
  return this.userMapper.mapPrismaUserToDomain(user); // Returns PortalUser
}
```

### Phase 4: Delete Old Files

#### 4.1 Delete from customer domain
- `packages/domain/customer/schema.ts` (consolidated into types.ts)
- `packages/domain/customer/contract.ts` (consolidated into types.ts)

#### 4.2 Delete from auth domain
- `packages/domain/auth/schema.ts` (consolidated into types.ts)
- `packages/domain/auth/contract.ts` (consolidated into types.ts)
- `packages/domain/auth/providers/` (moved to customer domain)

#### 4.3 Delete from BFF (already done in previous cleanup)
- Redundant type files already removed

### Phase 5: Verification

#### 5.1 TypeScript Compilation
```bash
cd packages/domain && npx tsc --noEmit
cd apps/bff && npx tsc --noEmit
```

#### 5.2 Import Verification
Verify:
- ✅ No imports of `PortalUser` or `UserProfile` from `@customer-portal/domain/auth`
- ✅ All user entity imports come from `@customer-portal/domain/customer`
- ✅ Auth domain only exports authentication mechanism types
- ✅ BFF constructs UserProfile directly via object creation

#### 5.3 Provider Pattern Consistency
Verify all domains follow provider pattern:
- ✅ `customer/providers/portal/` - Prisma → PortalUser
- ✅ `customer/providers/whmcs/` - WHMCS → CustomerProfile
- ✅ Other domains (billing, subscriptions) have consistent provider structure

## Summary of Changes

### What's Moving
- `PortalUser` type: `auth/` → `customer/`
- `UserProfile` type: `auth/` → `customer/`
- Portal (Prisma) provider: `auth/providers/prisma/` → `customer/providers/portal/`
- All schemas: `*/schema.ts` + `*/contract.ts` → `*/types.ts` (consolidated)

### What's Staying
- Auth domain: Only authentication mechanisms (login, signup, tokens, password, MFA)
- Customer domain: All user/customer entity types + providers

### New Components
- `customer/types.ts` - Single file with all schemas and types (including PortalUser, UserProfile)
- `customer/providers/portal/` - Portal DB provider for PortalUser
- `auth/types.ts` - Single file with all auth mechanism types (no user entities)

## Benefits

1. **Clear Separation of Concerns**
   - Auth = "How do I authenticate?" (mechanisms)
   - Customer = "Who am I?" (entities)

2. **Provider Pattern Consistency**
   - Portal provider (Prisma) alongside WHMCS provider
   - Same entity, different data sources

3. **Simplified File Structure**
   - One `types.ts` instead of `schema.ts` + `contract.ts`
   - Less file navigation

4. **Simple & Direct**
   - No unnecessary abstraction layers
   - Direct object construction with type safety

5. **Standard DDD**
   - Customer is the aggregate root
   - Auth is a mechanism that references customer

