542 lines
17 KiB
Markdown
542 lines
17 KiB
Markdown
|
|
# 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
|
||
|
|
|