# Package Organization & "Lib" Files Strategy

**Status**: ✅ Implemented  
**Date**: October 2025

---

## 🎯 Core Principle: Domain Package = Pure Domain Logic

The `@customer-portal/domain` package should contain **ONLY** pure domain logic that is:

- ✅ Framework-agnostic
- ✅ Reusable across frontend and backend
- ✅ Pure TypeScript (no React, no NestJS, no Next.js)
- ✅ No external infrastructure dependencies

---

## 📦 Package Structure Matrix

| Type                   | Location                                        | Examples                                  | Reasoning                                        |
| ---------------------- | ----------------------------------------------- | ----------------------------------------- | ------------------------------------------------ |
| **Domain Types**       | `packages/domain/*/contract.ts`                 | `Invoice`, `Order`, `Customer`            | Pure business entities                           |
| **Validation Schemas** | `packages/domain/*/schema.ts`                   | `invoiceSchema`, `orderQueryParamsSchema` | Runtime validation                               |
| **Pure Utilities**     | `packages/domain/toolkit/`                      | `formatCurrency()`, `parseDate()`         | No framework dependencies                        |
| **Provider Mappers**   | `packages/domain/*/providers/`                  | `transformWhmcsInvoice()`                 | Data transformation logic                        |
| **Provider Internals** | `packages/domain/common/providers/whmcs-utils/` | WHMCS parsing/serialization helpers       | Keep provider-only helpers shared but not public |
| **Framework Utils**    | `apps/*/src/lib/` or `apps/*/src/core/`         | API clients, React hooks                  | Framework-specific code                          |
| **Shared Infra**       | `packages/validation`, `packages/logging`       | `ZodPipe`, `useZodForm`                   | Framework bridges                                |

---

## 📂 Detailed Breakdown

### ✅ **What Belongs in `packages/domain/`**

#### 1. Domain Types & Contracts

```typescript
// packages/domain/billing/contract.ts
export interface Invoice {
  id: number;
  status: InvoiceStatus;
  total: number;
}
```

#### 2. Validation Schemas (Zod)

```typescript
// packages/domain/billing/schema.ts
export const invoiceSchema = z.object({
  id: z.number(),
  status: invoiceStatusSchema,
  total: z.number(),
});

// Domain-specific query params
export const invoiceQueryParamsSchema = z.object({
  page: z.coerce.number().int().positive().optional(),
  status: invoiceStatusSchema.optional(),
  dateFrom: z.string().datetime().optional(),
});
```

#### 3. Pure Utility Functions

```typescript
// packages/domain/toolkit/formatting/currency.ts
export function formatCurrency(amount: number, currency: SupportedCurrency): string {
  return new Intl.NumberFormat("en-US", {
    style: "currency",
    currency,
  }).format(amount);
}
```

✅ **Pure function** - no React, no DOM, no framework
✅ **Business logic** - directly related to domain entities
✅ **Reusable** - both frontend and backend can use it

#### 4. Provider Mappers

```typescript
// packages/domain/billing/providers/whmcs/mapper.ts
export function transformWhmcsInvoice(raw: WhmcsInvoiceRaw): Invoice {
  return {
    id: raw.invoiceid,
    status: mapStatus(raw.status),
    total: parseFloat(raw.total),
  };
}
```

---

### ❌ **What Should NOT Be in `packages/domain/`**

#### 1. Framework-Specific API Clients

```typescript
// ❌ DO NOT put in domain
// apps/portal/src/lib/api/client.ts
import { ApiClient } from "@hey-api/client-fetch"; // ← Framework dependency

export const apiClient = new ApiClient({
  baseUrl: process.env.NEXT_PUBLIC_API_URL, // ← Next.js specific
});
```

**Why?**

- Depends on `@hey-api/client-fetch` (external library)
- Uses Next.js environment variables
- Runtime infrastructure code

#### 2. React Hooks

```typescript
// ❌ DO NOT put in domain
// apps/portal/src/lib/hooks/useInvoices.ts
import { useQuery } from "@tanstack/react-query";  // ← React dependency

export function useInvoices() {
  return useQuery({ ... });  // ← React-specific
}
```

**Why?** React-specific - backend can't use this

#### 3. Error Handling with Framework Dependencies

```typescript
// ❌ DO NOT put in domain
// apps/portal/src/lib/utils/error-handling.ts
import { ApiError as ClientApiError } from "@/lib/api"; // ← Framework client

export function getErrorInfo(error: unknown): ApiErrorInfo {
  if (error instanceof ClientApiError) {
    // ← Framework-specific error type
    // ...
  }
}
```

**Why?** Depends on the API client implementation

#### 4. NestJS-Specific Utilities

```typescript
// ❌ DO NOT put in domain
// apps/bff/src/core/utils/error.util.ts
export function getErrorMessage(error: unknown): string {
  // Generic error extraction - could be in domain
  if (error instanceof Error) {
    return error.message;
  }
  return String(error);
}
```

**This one is borderline** - it's generic enough it COULD be in domain, but:

- Only used by backend
- Not needed for type definitions
- Better to keep application-specific utils in apps

---

## 🎨 Current Architecture (Correct!)

```
packages/
├── domain/                          # ✅ Pure domain logic
│   ├── billing/
│   │   ├── contract.ts             # ✅ Types
│   │   ├── schema.ts               # ✅ Zod schemas + domain query params
│   │   └── providers/whmcs/        # ✅ Data mappers
│   ├── common/
│   │   ├── types.ts                # ✅ Truly generic types (ApiResponse, PaginationParams)
│   │   └── schema.ts               # ✅ Truly generic schemas (paginationParamsSchema, emailSchema)
│   └── toolkit/
│       ├── formatting/             # ✅ Pure functions (formatCurrency, formatDate)
│       └── validation/             # ✅ Pure validation helpers
│
├── validation/                      # ✅ Framework bridges
│   ├── src/zod-pipe.ts             # NestJS Zod pipe
│   └── src/zod-form.ts             # React Zod form hook
│
└── logging/                         # ✅ Infrastructure
    └── src/logger.ts                # Logging utilities

apps/
├── portal/ (Next.js)
│   └── src/lib/
│       ├── api/                    # ❌ Framework-specific
│       │   ├── client.ts           # API client instance
│       │   └── runtime/            # Generated client code
│       ├── hooks/                  # ❌ React-specific
│       │   └── useAuth.ts          # React Query hooks
│       └── utils/                  # ❌ App-specific
│           ├── error-handling.ts   # Portal error handling
│           └── cn.ts               # Tailwind utility
│
└── bff/ (NestJS)
    └── src/core/
        ├── validation/             # ❌ NestJS-specific
        │   └── zod-validation.filter.ts
        └── utils/                  # ❌ App-specific
            ├── error.util.ts       # BFF error utilities
            └── validation.util.ts  # BFF validation helpers
```

---

## 🔄 Decision Framework: "Should This Be in Domain?"

Ask these questions:

### ✅ Move to Domain If:

1. Is it a **pure function** with no framework dependencies?
2. Does it work with **domain types**?
3. Could **both frontend and backend** use it?
4. Is it **business logic** (not infrastructure)?

### ❌ Keep in Apps If:

1. Does it import **React**, **Next.js**, or **NestJS**?
2. Does it use **environment variables**?
3. Does it depend on **external libraries** (API clients, HTTP libs)?
4. Is it **UI-specific** or **framework-specific**?
5. Is it only used in **one app**?

---

## 📋 Examples with Decisions

| Utility                            | Decision      | Location                          | Why                        |
| ---------------------------------- | ------------- | --------------------------------- | -------------------------- |
| `formatCurrency(amount, currency)` | ✅ Domain     | `domain/toolkit/formatting/`      | Pure function, no deps     |
| `invoiceQueryParamsSchema`         | ✅ Domain     | `domain/billing/schema.ts`        | Domain-specific validation |
| `paginationParamsSchema`           | ✅ Domain     | `domain/common/schema.ts`         | Truly generic              |
| `useInvoices()` React hook         | ❌ Portal App | `portal/lib/hooks/`               | React-specific             |
| `apiClient` instance               | ❌ Portal App | `portal/lib/api/`                 | Framework-specific         |
| `ZodValidationPipe`                | ❌ BFF App    | `apps/bff/src/app.module.ts`      | NestJS/framework-specific  |
| `getErrorMessage(error)`           | ❌ BFF App    | `bff/core/utils/`                 | App-specific utility       |
| `transformWhmcsInvoice()`          | ✅ Domain     | `domain/billing/providers/whmcs/` | Data transformation        |

---

## 🚀 What About Query Parameters?

### Current Structure (✅ Correct!)

**Generic building blocks** in `domain/common/`:

```typescript
// packages/domain/common/schema.ts
export const paginationParamsSchema = z.object({
  page: z.coerce.number().int().positive().optional(),
  limit: z.coerce.number().int().positive().max(100).optional(),
});

export const filterParamsSchema = z.object({
  search: z.string().optional(),
  sortBy: z.string().optional(),
  sortOrder: z.enum(["asc", "desc"]).optional(),
});
```

**Domain-specific query params** in their own domains:

```typescript
// packages/domain/billing/schema.ts
export const invoiceQueryParamsSchema = z.object({
  page: z.coerce.number().int().positive().optional(),
  limit: z.coerce.number().int().positive().max(100).optional(),
  status: invoiceStatusSchema.optional(), // ← Domain-specific
  dateFrom: z.string().datetime().optional(), // ← Domain-specific
  dateTo: z.string().datetime().optional(), // ← Domain-specific
});

// packages/domain/subscriptions/schema.ts
export const subscriptionQueryParamsSchema = z.object({
  page: z.coerce.number().int().positive().optional(),
  limit: z.coerce.number().int().positive().max(100).optional(),
  status: subscriptionStatusSchema.optional(), // ← Domain-specific
  type: z.string().optional(), // ← Domain-specific
});
```

**Why this works:**

- `common` has truly generic utilities
- Each domain owns its specific query parameters
- No duplication of business logic

---

## 📖 Summary

### Domain Package (`packages/domain/`)

**Contains:**

- ✅ Domain types & interfaces
- ✅ Zod validation schemas
- ✅ Provider mappers (WHMCS, Salesforce, Freebit)
- ✅ Pure utility functions (formatting, parsing)
- ✅ Domain-specific query parameter schemas

**Does NOT contain:**

- ❌ React hooks
- ❌ API client instances
- ❌ Framework-specific code
- ❌ Infrastructure code

### App Lib/Core Directories

**Contains:**

- ✅ Framework-specific utilities
- ✅ API clients & HTTP interceptors
- ✅ React hooks & custom hooks
- ✅ Error handling with framework dependencies
- ✅ Application-specific helpers

---

**Key Takeaway**: The domain package is your **single source of truth for types and validation**. Everything else stays in apps where it belongs!
