292 lines
9.4 KiB
Markdown
292 lines
9.4 KiB
Markdown
|
|
# 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**
|
||
|
|
- Domain contracts, schemas, and provider logic live together
|
||
|
|
- Easy to find related code
|
||
|
|
- Clear ownership and responsibility
|
||
|
|
|
||
|
|
### 2. **Provider Isolation**
|
||
|
|
- Raw types and mappers nested in `providers/` subfolder
|
||
|
|
- Each provider is self-contained
|
||
|
|
- Easy to add/remove providers
|
||
|
|
|
||
|
|
### 3. **Type Safety**
|
||
|
|
- Zod schemas for runtime validation
|
||
|
|
- TypeScript types inferred from schemas
|
||
|
|
- Branded types for stronger type checking
|
||
|
|
|
||
|
|
### 4. **Clean Exports**
|
||
|
|
- Barrel exports (`index.ts`) control public API
|
||
|
|
- Provider mappers exported as namespaces (`WhmcsBillingMapper.*`)
|
||
|
|
- Predictable import paths
|
||
|
|
|
||
|
|
### 5. **Minimal Dependencies**
|
||
|
|
- Only depends on `zod` for runtime validation
|
||
|
|
- No circular dependencies
|
||
|
|
- Self-contained domain logic
|
||
|
|
|
||
|
|
## 📋 Domain Reference
|
||
|
|
|
||
|
|
### Billing
|
||
|
|
- **Contracts**: `Invoice`, `InvoiceItem`, `InvoiceList`
|
||
|
|
- **Providers**: WHMCS
|
||
|
|
- **Use Cases**: Display invoices, payment history, invoice details
|
||
|
|
|
||
|
|
### Subscriptions
|
||
|
|
- **Contracts**: `Subscription`, `SubscriptionList`
|
||
|
|
- **Providers**: WHMCS
|
||
|
|
- **Use Cases**: Display active services, manage subscriptions
|
||
|
|
|
||
|
|
### Payments
|
||
|
|
- **Contracts**: `PaymentMethod`, `PaymentGateway`
|
||
|
|
- **Providers**: WHMCS
|
||
|
|
- **Use Cases**: Payment method management, gateway configuration
|
||
|
|
|
||
|
|
### SIM
|
||
|
|
- **Contracts**: `SimDetails`, `SimUsage`, `SimTopUpHistory`
|
||
|
|
- **Providers**: Freebit
|
||
|
|
- **Use Cases**: SIM management, usage tracking, top-up history
|
||
|
|
|
||
|
|
### Orders
|
||
|
|
- **Contracts**: `OrderSummary`, `OrderDetails`, `FulfillmentOrderDetails`
|
||
|
|
- **Providers**: WHMCS (provisioning), Salesforce (order management)
|
||
|
|
- **Use Cases**: Order fulfillment, order history, order details
|
||
|
|
|
||
|
|
### Catalog
|
||
|
|
- **Contracts**: `InternetPlanCatalogItem`, `SimCatalogProduct`, `VpnCatalogProduct`
|
||
|
|
- **Providers**: Salesforce (Product2)
|
||
|
|
- **Use Cases**: Product catalog display, product selection
|
||
|
|
|
||
|
|
### Common
|
||
|
|
- **Types**: `IsoDateTimeString`, `UserId`, `AccountId`, `OrderId`, `ApiResponse`, `PaginatedResponse`
|
||
|
|
- **Use Cases**: Shared utility types across all domains
|
||
|
|
|
||
|
|
### Toolkit
|
||
|
|
- **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:**
|
||
|
|
```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:**
|
||
|
|
```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
|
||
|
|
- **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)
|
||
|
|
|