307 lines
9.5 KiB
Markdown
Raw Permalink Normal View History

# 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
├── services/
│ ├── 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 { InternetPlanCatalogItem } from "@customer-portal/domain/services";
```
### 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
### Services
- **Contracts**: `InternetPlanCatalogItem`, `SimCatalogProduct`, `VpnCatalogProduct`
- **Providers**: Salesforce (Product2)
- **Use Cases**: Product catalog display, product selection, service browsing
### 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)