- Added a new script command `dev:studio` to facilitate studio management in development. - Updated the Prisma dependency in BFF package to version 6.16.0 for improved features and bug fixes. - Refactored Dockerfiles for BFF and Portal to enhance health check mechanisms during startup. - Removed deprecated WhmcsApiMethodsService to streamline the integration services. - Cleaned up various components and services in the SIM management module for better maintainability and clarity.
Domain Toolkit
Utility functions and helpers for the domain layer. This package contains utilities only, not validation.
Purpose
The toolkit provides pure utility functions for common operations like:
- String manipulation
- Date/time formatting
- Currency formatting
- URL parsing and manipulation
- Type guards and assertions
Important: For validation functions (e.g., isValidEmail, isValidUuid), use packages/domain/common/validation.ts instead.
Structure
toolkit/
├── formatting/ # Formatting utilities
│ ├── currency.ts # Currency formatting (e.g., formatCurrency)
│ ├── date.ts # Date formatting (e.g., formatDate, formatRelativeTime)
│ ├── phone.ts # Phone number formatting
│ └── text.ts # Text manipulation (e.g., truncate, capitalize)
│
├── typing/ # Type utilities
│ ├── assertions.ts # Type assertions (e.g., assertNonNull)
│ ├── guards.ts # Type guards (e.g., isString, isNumber)
│ └── helpers.ts # Type helper functions
│
└── validation/ # Validation utilities (NOT validation itself)
├── email.ts # Email utilities (getEmailDomain, normalizeEmail)
├── url.ts # URL utilities (ensureProtocol, getHostname)
├── string.ts # String utilities (isEmpty, hasMinLength)
└── helpers.ts # Validation helpers (sanitizePagination, isInRange)
Important Distinction: Validation vs Utilities
❌ Validation (NOT in toolkit)
Validation determines if input is valid or invalid:
// These are VALIDATION functions → Use common/validation.ts
isValidEmail(email) // Returns true/false
isValidUuid(id) // Returns true/false
isValidUrl(url) // Returns true/false
validateUrlOrThrow(url) // Throws if invalid
Location: packages/domain/common/validation.ts
✅ Utilities (IN toolkit)
Utilities transform, extract, or manipulate data:
// These are UTILITY functions → Use toolkit
getEmailDomain(email) // Extracts domain from email
normalizeEmail(email) // Lowercases and trims
ensureProtocol(url) // Adds https:// if missing
getHostname(url) // Extracts hostname
Location: packages/domain/toolkit/
Usage Examples
Formatting
import { formatCurrency, formatDate } from "@customer-portal/domain/toolkit/formatting";
const price = formatCurrency(1000, "JPY"); // "¥1,000"
const date = formatDate(new Date()); // "2025-10-08"
Type Guards
import { isString, isNumber } from "@customer-portal/domain/toolkit/typing";
if (isString(value)) {
// TypeScript knows value is string here
console.log(value.toUpperCase());
}
URL Utilities
import { ensureProtocol, getHostname } from "@customer-portal/domain/toolkit/validation/url";
const fullUrl = ensureProtocol("example.com"); // "https://example.com"
const host = getHostname("https://example.com/path"); // "example.com"
Email Utilities
import { getEmailDomain, normalizeEmail } from "@customer-portal/domain/toolkit/validation/email";
const domain = getEmailDomain("user@example.com"); // "example.com"
const normalized = normalizeEmail(" User@Example.COM "); // "user@example.com"
When to Use What
| Task | Use | Example |
|---|---|---|
| Validate email format | common/validation.ts |
isValidEmail(email) |
| Extract email domain | toolkit/validation/email.ts |
getEmailDomain(email) |
| Validate URL format | common/validation.ts |
isValidUrl(url) |
| Add protocol to URL | toolkit/validation/url.ts |
ensureProtocol(url) |
| Format currency | toolkit/formatting/currency.ts |
formatCurrency(amount, "JPY") |
| Format date | toolkit/formatting/date.ts |
formatDate(date) |
Best Practices
-
Use validation functions for validation
// ✅ Good import { isValidEmail } from "@customer-portal/domain/common/validation"; if (!isValidEmail(email)) throw new Error("Invalid email"); // ❌ Bad - don't write custom validation if (!email.includes("@")) throw new Error("Invalid email"); -
Use utility functions for transformations
// ✅ Good import { normalizeEmail } from "@customer-portal/domain/toolkit/validation/email"; const clean = normalizeEmail(email); // ❌ Bad - don't duplicate utility logic const clean = email.trim().toLowerCase(); -
Don't mix validation and utilities
// ❌ Bad - mixing concerns function processEmail(email: string) { if (!email.includes("@")) return null; // Validation return email.toLowerCase(); // Utility } // ✅ Good - separate concerns import { isValidEmail } from "@customer-portal/domain/common/validation"; import { normalizeEmail } from "@customer-portal/domain/toolkit/validation/email"; function processEmail(email: string) { if (!isValidEmail(email)) return null; return normalizeEmail(email); }
Migration Notes
If you're refactoring existing code:
- Validation functions → Move to
common/validation.ts - Utility functions → Keep in or move to
toolkit/ - Business logic → Move to domain-specific
validation.tsfiles
Related Documentation
- Validation Patterns Guide - How to implement validation
- Domain Layer Design - Overall domain architecture
- Common Validation - Validation functions
Questions?
If you're unsure whether something belongs in toolkit or common/validation:
-
Ask: "Does this determine if input is valid/invalid?"
- YES → It's validation → Use
common/validation.ts - NO → It's a utility → Use
toolkit/
- YES → It's validation → Use
-
Ask: "Does this transform or extract data?"
- YES → It's a utility → Use
toolkit/ - NO → Might be validation → Use
common/validation.ts
- YES → It's a utility → Use