# Codebase Cleanup Analysis - Unnecessary Abstractions ## Executive Summary After comprehensive audit, identified several categories of unnecessary abstractions that add complexity without value. This document catalogs findings and proposes removals. --- ## ✅ Already Fixed ### 1. **Checkout Normalizers** (Removed ~280 lines) - ❌ `buildInternetCheckoutSelections()` - Trivial string trimming - ❌ `buildSimCheckoutSelections()` - Trivial string trimming - ❌ `deriveInternetCheckoutState()` - Reverse transformations - ❌ `deriveSimCheckoutState()` - Reverse transformations - ❌ `coalescePlanSku()` - Obsolete after plan/planSku cleanup **Impact**: Frontend now directly builds URLSearchParams without domain layer interference. --- ## 🔍 Findings by Category ### Category A: Thin Service Wrappers (Keep - Have Purpose) #### ✅ **KEEP** these services - they add value: **`apps/portal/src/features/*/services/*.service.ts`**: - `accountService` - Centralizes API endpoints, good - `checkoutService` - Wraps API calls, provides error handling - `ordersService` - Validates with Zod schemas post-fetch - `catalogService` - Parses and validates catalog data with domain parsers - `simActionsService` - Type-safe API wrappers **Why keep?**: 1. Centralized API endpoint management 2. Error handling and logging 3. Response validation with Zod 4. Type safety layer between API and components ### Category B: Unnecessary Utility Wrappers #### ❌ **CurrencyService** - Remove class wrapper **Current** (`apps/portal/src/lib/services/currency.service.ts`): ```typescript class CurrencyServiceImpl implements CurrencyService { async getDefaultCurrency(): Promise { const response = await apiClient.GET("/api/currency/default"); if (!response.data) throw new Error("..."); return response.data as CurrencyInfo; } // ... } export const currencyService = new CurrencyServiceImpl(); ``` **Problem**: - Unnecessary class + interface abstraction - Just wraps apiClient calls - No business logic, no validation - Instance creation overhead **Fix**: Make it a plain object like other services: ```typescript export const currencyService = { async getDefaultCurrency(): Promise { const response = await apiClient.GET("/api/currency/default"); return getDataOrThrow(response, "Failed to get default currency"); }, // ... }; ``` **Impact**: Remove 10 lines, simpler, consistent with other services. --- ### Category C: Trivial Utility Functions #### ❌ **cn() utility** - Questionable value **Current** (`apps/portal/src/lib/utils/cn.ts`): ```typescript export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)); } ``` **Analysis**: - Just combines two library calls - Used everywhere, hard to remove now - Consider: Is `twMerge(clsx(...))` really so hard to type? **Verdict**: KEEP - too embedded, minimal harm. But document that new projects shouldn't add such thin wrappers. #### ✅ **useDebounce** - Keep, adds real value Implements actual logic (setTimeout management), not just a wrapper. #### ✅ **useLocalStorage** - Keep, adds real value Handles SSR safety, error handling, JSON serialization - significant logic. --- ### Category D: Potential Over-Engineering #### 🤔 **CheckoutParamsService** - Could simplify **Current** (`apps/portal/src/features/checkout/services/checkout-params.service.ts`): - Static class with utility methods - Pattern: `CheckoutParamsService.buildSnapshot(params)` **Question**: Why a class? Could be plain functions: ```typescript // Instead of: CheckoutParamsService.buildSnapshot(params) // Could be: buildCheckoutSnapshot(params) ``` **Verdict**: KEEP for now - class provides namespace, not harmful enough to refactor. --- ## 🎯 Domain Layer Audit ### ✅ **Domain Layer is Clean!** **Findings**: - ✅ No `window`, `document`, `localStorage` references - ✅ No React/Next.js dependencies - ✅ No portal-specific logic - ✅ Pure TypeScript + Zod - ✅ Properly separated providers (Salesforce, WHMCS, Freebit) **Only "next" references are in README examples - not actual code.** ### Domain Layer Best Practices (Already Following): 1. **Provider Pattern** ✅ - `packages/domain/*/providers/` - Clean separation - Mappers transform external APIs to domain types - Example: `transformWhmcsInvoice()`, `transformSalesforceOrder()` 2. **Schema-First Design** ✅ - All types derived from Zod schemas - Runtime validation at boundaries - Example: `invoiceSchema`, `orderDetailsSchema` 3. **No Frontend Concerns** ✅ - No URL handling - No localStorage - No React hooks --- ## 📊 Summary Statistics | Category | Status | Lines Removed | Impact | |----------|--------|---------------|--------| | Checkout normalizers | ✅ Removed | ~280 | High - Simplified flow | | Catalog store refactor | ✅ Simplified | ~50 | Medium - Direct params | | Checkout params service | ✅ Simplified | ~20 | Low - Removed coalesce | | CurrencyService | ⚠️ Can remove | ~10 | Low - Minor improvement | | Domain layer | ✅ Already clean | 0 | N/A | **Total Cleanup**: ~350 lines removed, significant complexity reduction. --- ## 🎯 Recommendations ### Immediate Actions (Already Done ✅) 1. ✅ Remove `buildInternetCheckoutSelections` and friends 2. ✅ Simplify catalog store URL param building 3. ✅ Remove `coalescePlanSku` backward compat 4. ✅ Clean up `checkout.ts` domain file ### Optional Future Actions 1. **Simplify CurrencyService**: Remove class wrapper (~10 lines) 2. **Document anti-patterns**: Add to CONTRIBUTING.md - Don't wrap library functions (like `cn()`) - Avoid unnecessary normalizers - Frontend handles URL serialization ### Best Practices Going Forward #### ✅ DO: - Keep services that centralize API endpoints - Keep services that add validation/error handling - Keep utilities that implement real logic - Use domain layer for types and validation only #### ❌ DON'T: - Create normalizers that just trim strings - Wrap simple library calls in custom functions - Put frontend concerns in domain layer - Create class wrappers for simple API calls --- ## 🏆 Domain/Portal Alignment ### Current State: **EXCELLENT** ✅ ``` packages/domain/ # Pure business logic ├── */contract.ts # TypeScript interfaces ├── */schema.ts # Zod validation ├── */providers/ # External API adapters └── */helpers.ts # Domain utilities apps/portal/src/ ├── features/*/services/ # API client wrappers (good!) ├── features/*/hooks/ # React hooks (frontend-specific) ├── features/*/components/ # UI components └── lib/ # Portal utilities ``` **Key Separation**: - Domain: Types, validation, transformations - Portal: API calls, UI state, React hooks **No violations found!** The architecture is properly layered. --- ## 📝 Conclusion The codebase is **generally well-structured**. Main issues were: 1. ✅ **Fixed**: Checkout normalizers (unnecessary abstractions) 2. ⚠️ **Minor**: CurrencyService class wrapper (optional fix) 3. ✅ **Already Good**: Domain layer separation **Overall Grade**: B+ → A- after cleanup The cleanup removed ~350 lines of unnecessary abstraction while maintaining all valuable service layers. Domain and portal are properly aligned with no cross-contamination.