2025-10-03 14:26:55 +09:00
# New Domain Architecture
## Overview
The `@customer-portal/domain` package is now a **unified domain layer** following the **Provider-Aware Structure** pattern. It consolidates all types, schemas, and provider-specific logic into a single, well-organized package.
## 🎯 Goals
1. **Single Source of Truth** : One place for all domain contracts, schemas, and transformations
2. **Provider Isolation** : Raw provider types and mappers co-located within each domain
3. **Clean Exports** : Simple, predictable import paths
4. **Type Safety** : Runtime validation with Zod + TypeScript inference
5. **Maintainability** : Easy to find and update domain logic
## 📁 Structure
```
packages/domain/
├── billing/
│ ├── contract.ts # Invoice, InvoiceItem, InvoiceList
│ ├── schema.ts # Zod schemas
│ ├── providers/
│ │ └── whmcs/
│ │ ├── raw.types.ts # WHMCS-specific types
│ │ └── mapper.ts # Transform WHMCS → Invoice
│ └── index.ts # Public exports
├── subscriptions/
│ ├── contract.ts # Subscription, SubscriptionList
│ ├── schema.ts
│ ├── providers/
│ │ └── whmcs/
│ │ ├── raw.types.ts
│ │ └── mapper.ts
│ └── index.ts
├── payments/
│ ├── contract.ts # PaymentMethod, PaymentGateway
│ ├── schema.ts
│ ├── providers/
│ │ └── whmcs/
│ │ ├── raw.types.ts
│ │ └── mapper.ts
│ └── index.ts
├── sim/
│ ├── contract.ts # SimDetails, SimUsage, SimTopUpHistory
│ ├── schema.ts
│ ├── providers/
│ │ └── freebit/
│ │ ├── raw.types.ts
│ │ └── mapper.ts
│ └── index.ts
├── orders/
│ ├── contract.ts # OrderSummary, OrderDetails, FulfillmentOrder
│ ├── schema.ts
│ ├── providers/
│ │ ├── whmcs/
│ │ │ ├── raw.types.ts # WHMCS AddOrder API types
│ │ │ └── mapper.ts # Transform FulfillmentOrder → WHMCS
│ │ └── salesforce/
│ │ ├── raw.types.ts # Salesforce Order/OrderItem
│ │ └── mapper.ts # Transform Salesforce → OrderDetails
│ └── index.ts
├── catalog/
│ ├── contract.ts # CatalogProduct, InternetPlan, SimProduct, VpnProduct
│ ├── schema.ts
│ ├── providers/
│ │ └── salesforce/
│ │ ├── raw.types.ts # Salesforce Product2
│ │ └── mapper.ts # Transform Product2 → CatalogProduct
│ └── index.ts
├── common/
│ ├── types.ts # IsoDateTimeString, branded types, API wrappers
│ └── index.ts
├── toolkit/
│ ├── formatting/ # Currency, date, phone, text formatters
│ ├── validation/ # Email, URL, string validators
│ ├── typing/ # Type guards, assertions, helpers
│ └── index.ts
├── package.json
├── tsconfig.json
└── index.ts # Main entry point
```
## 📦 Usage
### Import Contracts
```typescript
// Import domain contracts
import type { Invoice, InvoiceList } from "@customer -portal/domain/billing";
import type { Subscription } from "@customer -portal/domain/subscriptions";
import type { SimDetails } from "@customer -portal/domain/sim";
import type { OrderSummary } from "@customer -portal/domain/orders";
import type { CatalogProduct } from "@customer -portal/domain/catalog";
```
### Import Schemas
```typescript
// Import Zod schemas for runtime validation
import { invoiceSchema, invoiceListSchema } from "@customer -portal/domain/billing";
import { simDetailsSchema } from "@customer -portal/domain/sim";
// Validate at runtime
const invoice = invoiceSchema.parse(rawData);
```
### Import Provider Mappers
```typescript
// Import provider-specific mappers
import { WhmcsBillingMapper } from "@customer -portal/domain/billing";
import { FreebitSimMapper } from "@customer -portal/domain/sim";
import { WhmcsOrderMapper, SalesforceOrderMapper } from "@customer -portal/domain/orders";
// Transform provider data to normalized domain contracts
const invoice = WhmcsBillingMapper.transformWhmcsInvoice(whmcsInvoiceData);
const simDetails = FreebitSimMapper.transformFreebitAccountDetails(freebitAccountData);
const order = SalesforceOrderMapper.transformOrderToDetails(salesforceOrderRecord, items);
```
### Import Utilities
```typescript
// Import toolkit utilities
import { Formatting, Validation, Typing } from "@customer -portal/domain/toolkit";
// Use formatters
const formatted = Formatting.formatCurrency(10000, "JPY");
const date = Formatting.formatDate(isoString);
// Use validators
const isValid = Validation.isValidEmail(email);
// Use type guards
if (Typing.isDefined(value)) {
// TypeScript knows value is not null/undefined
}
```
## 🔄 Data Flow
### Inbound (Provider → Domain)
```
External API Response
↓
Raw Provider Types (providers/*/raw.types.ts)
↓
Provider Mapper (providers/*/mapper.ts)
↓
Zod Schema Validation (schema.ts)
↓
Domain Contract (contract.ts)
↓
Application Code (BFF, Portal)
```
### Outbound (Domain → Provider)
```
Application Intent
↓
Domain Contract (contract.ts)
↓
Provider Mapper (providers/*/mapper.ts)
↓
Raw Provider Payload (providers/*/raw.types.ts)
↓
External API Request
```
## 🎨 Design Principles
### 1. **Co-location**
2025-12-23 15:43:36 +09:00
2025-10-03 14:26:55 +09:00
- Domain contracts, schemas, and provider logic live together
- Easy to find related code
- Clear ownership and responsibility
### 2. **Provider Isolation**
2025-12-23 15:43:36 +09:00
2025-10-03 14:26:55 +09:00
- Raw types and mappers nested in `providers/` subfolder
- Each provider is self-contained
- Easy to add/remove providers
### 3. **Type Safety**
2025-12-23 15:43:36 +09:00
2025-10-03 14:26:55 +09:00
- Zod schemas for runtime validation
- TypeScript types inferred from schemas
- Branded types for stronger type checking
### 4. **Clean Exports**
2025-12-23 15:43:36 +09:00
2025-10-03 14:26:55 +09:00
- Barrel exports (`index.ts` ) control public API
- Provider mappers exported as namespaces (`WhmcsBillingMapper.*` )
- Predictable import paths
### 5. **Minimal Dependencies**
2025-12-23 15:43:36 +09:00
2025-10-03 14:26:55 +09:00
- Only depends on `zod` for runtime validation
- No circular dependencies
- Self-contained domain logic
## 📋 Domain Reference
### Billing
2025-12-23 15:43:36 +09:00
2025-10-03 14:26:55 +09:00
- **Contracts**: `Invoice` , `InvoiceItem` , `InvoiceList`
- **Providers**: WHMCS
- **Use Cases**: Display invoices, payment history, invoice details
### Subscriptions
2025-12-23 15:43:36 +09:00
2025-10-03 14:26:55 +09:00
- **Contracts**: `Subscription` , `SubscriptionList`
- **Providers**: WHMCS
- **Use Cases**: Display active services, manage subscriptions
### Payments
2025-12-23 15:43:36 +09:00
2025-10-03 14:26:55 +09:00
- **Contracts**: `PaymentMethod` , `PaymentGateway`
- **Providers**: WHMCS
- **Use Cases**: Payment method management, gateway configuration
### SIM
2025-12-23 15:43:36 +09:00
2025-10-03 14:26:55 +09:00
- **Contracts**: `SimDetails` , `SimUsage` , `SimTopUpHistory`
- **Providers**: Freebit
- **Use Cases**: SIM management, usage tracking, top-up history
### Orders
2025-12-23 15:43:36 +09:00
2025-10-03 14:26:55 +09:00
- **Contracts**: `OrderSummary` , `OrderDetails` , `FulfillmentOrderDetails`
- **Providers**: WHMCS (provisioning), Salesforce (order management)
- **Use Cases**: Order fulfillment, order history, order details
### Catalog
2025-12-23 15:43:36 +09:00
2025-10-03 14:26:55 +09:00
- **Contracts**: `InternetPlanCatalogItem` , `SimCatalogProduct` , `VpnCatalogProduct`
- **Providers**: Salesforce (Product2)
- **Use Cases**: Product catalog display, product selection
### Common
2025-12-23 15:43:36 +09:00
2025-10-03 14:26:55 +09:00
- **Types**: `IsoDateTimeString` , `UserId` , `AccountId` , `OrderId` , `ApiResponse` , `PaginatedResponse`
- **Use Cases**: Shared utility types across all domains
### Toolkit
2025-12-23 15:43:36 +09:00
2025-10-03 14:26:55 +09:00
- **Formatting**: Currency, date, phone, text formatters
- **Validation**: Email, URL, string validators
- **Typing**: Type guards, assertions, helpers
- **Use Cases**: UI formatting, input validation, type safety
## 🚀 Migration Guide
### From Old Structure
**Before:**
2025-12-23 15:43:36 +09:00
2025-10-03 14:26:55 +09:00
```typescript
import type { Invoice } from "@customer -portal/contracts/billing";
import { invoiceSchema } from "@customer -portal/schemas/business/billing.schema";
import { transformWhmcsInvoice } from "@customer -portal/integrations-whmcs/mappers/billing.mapper";
```
**After:**
2025-12-23 15:43:36 +09:00
2025-10-03 14:26:55 +09:00
```typescript
import type { Invoice } from "@customer -portal/domain/billing";
import { invoiceSchema } from "@customer -portal/domain/billing";
import { WhmcsBillingMapper } from "@customer -portal/domain/billing";
const invoice = WhmcsBillingMapper.transformWhmcsInvoice(data);
```
### Benefits
2025-12-23 15:43:36 +09:00
2025-10-03 14:26:55 +09:00
- **Fewer imports**: Everything in one package
- **Clearer intent**: Mapper namespace indicates provider
- **Better DX**: Autocomplete shows all related exports
- **Type safety**: Contracts + schemas + mappers always in sync
## ✅ Completed Domains
- ✅ Billing (WHMCS provider)
- ✅ Subscriptions (WHMCS provider)
- ✅ Payments (WHMCS provider)
- ✅ SIM (Freebit provider)
- ✅ Orders (WHMCS + Salesforce providers)
- ✅ Catalog (Salesforce provider)
- ✅ Common (shared types)
- ✅ Toolkit (utilities)
## 📝 Next Steps
1. **Update BFF imports** to use `@customer-portal/domain/*`
2. **Update Portal imports** to use `@customer-portal/domain/*`
3. **Delete old packages** : `contracts` , `schemas` , `integrations`
4. **Update ESLint rules** to prevent imports from old packages
5. **Update documentation** to reference new structure
## 🔗 Related Documentation
- [Provider-Aware Structure ](./DOMAIN-STRUCTURE.md )
- [Type Cleanup Summary ](./TYPE-CLEANUP-SUMMARY.md )
- [Architecture Overview ](./ARCHITECTURE.md )