- Added Cache-Control headers to various endpoints in CatalogController and SubscriptionsController to improve caching behavior and reduce server load. - Updated response structures to ensure consistent caching strategies across different API endpoints. - Improved overall performance by implementing throttling and caching mechanisms for better request management.
7.3 KiB
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, goodcheckoutService- Wraps API calls, provides error handlingordersService- Validates with Zod schemas post-fetchcatalogService- Parses and validates catalog data with domain parserssimActionsService- Type-safe API wrappers
Why keep?:
- Centralized API endpoint management
- Error handling and logging
- Response validation with Zod
- 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):
class CurrencyServiceImpl implements CurrencyService {
async getDefaultCurrency(): Promise<CurrencyInfo> {
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:
export const currencyService = {
async getDefaultCurrency(): Promise<CurrencyInfo> {
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):
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:
// 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,localStoragereferences - ✅ 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):
-
Provider Pattern ✅
packages/domain/*/providers/- Clean separation- Mappers transform external APIs to domain types
- Example:
transformWhmcsInvoice(),transformSalesforceOrder()
-
Schema-First Design ✅
- All types derived from Zod schemas
- Runtime validation at boundaries
- Example:
invoiceSchema,orderDetailsSchema
-
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 ✅)
- ✅ Remove
buildInternetCheckoutSelectionsand friends - ✅ Simplify catalog store URL param building
- ✅ Remove
coalescePlanSkubackward compat - ✅ Clean up
checkout.tsdomain file
Optional Future Actions
- Simplify CurrencyService: Remove class wrapper (~10 lines)
- Document anti-patterns: Add to CONTRIBUTING.md
- Don't wrap library functions (like
cn()) - Avoid unnecessary normalizers
- Frontend handles URL serialization
- Don't wrap library functions (like
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:
- ✅ Fixed: Checkout normalizers (unnecessary abstractions)
- ⚠️ Minor: CurrencyService class wrapper (optional fix)
- ✅ 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.