Assist_Design/CODEBASE_CLEANUP_ANALYSIS.md
barsa 2611e63cfd Enhance caching and response handling in catalog and subscriptions controllers
- 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.
2025-10-29 13:29:28 +09:00

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, 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):

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, 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.