T. Narantuya a95ec60859 Refactor address management and update related services for improved clarity and functionality
- Updated address retrieval in user service to replace billing info with a dedicated address method.
- Adjusted API endpoints to use `PATCH /api/me/address` for address updates instead of billing updates.
- Enhanced documentation to reflect changes in address management processes and API usage.
- Removed deprecated types and services related to billing address handling, streamlining the codebase.
2025-09-17 18:43:43 +09:00

10 KiB

Design Document

Overview

This design document outlines a comprehensive refactoring strategy to transform the current customer portal codebase into a modern, maintainable, and scalable architecture. The refactoring will eliminate redundancies, establish consistent patterns, and improve developer experience while maintaining existing functionality.

Architecture

High-Level Structure

The refactored architecture will follow a feature-driven, layered approach:

apps/portal/src/
├── app/                    # Next.js App Router pages
├── components/             # Shared UI components (Design System)
│   ├── ui/                # Base UI components (atoms)
│   ├── layout/            # Layout components (organisms)
│   └── common/            # Shared business components (molecules)
├── features/              # Feature-specific modules
│   ├── auth/              # Authentication feature
│   ├── dashboard/         # Dashboard feature
│   ├── billing/           # Billing feature
│   ├── subscriptions/     # Subscriptions feature
│   ├── catalog/           # Product catalog feature
│   └── support/           # Support feature
├── lib/                   # Core utilities and services
│   ├── api/               # API client and services
│   ├── auth/              # Authentication logic
│   ├── hooks/             # Shared React hooks
│   ├── stores/            # State management
│   ├── types/             # Shared TypeScript types
│   └── utils/             # Utility functions
├── providers/             # React context providers
└── styles/                # Global styles and design tokens

Design Principles

  1. Feature-First Organization: Group related functionality together
  2. Atomic Design: Build UI components in a hierarchical manner
  3. Separation of Concerns: Separate business logic from presentation
  4. Single Responsibility: Each module has one clear purpose
  5. Dependency Inversion: Depend on abstractions, not concretions

Components and Interfaces

Design System Architecture

Atomic Design Structure

// Base UI Components (Atoms)
components/ui/
├── Button/
   ├── Button.tsx
   ├── Button.stories.tsx
   ├── Button.test.tsx
   └── index.ts
├── Input/
├── Badge/
└── ...

// Composite Components (Molecules)
components/common/
├── DataTable/
├── SearchBar/
├── StatusIndicator/
└── ...

// Layout Components (Organisms)
components/layout/
├── DashboardLayout/
├── AuthLayout/
├── PageLayout/
└── ...

Component Interface Standards

// Base component props interface
interface BaseComponentProps {
  className?: string;
  children?: React.ReactNode;
  testId?: string;
}

// Variant-based component pattern
interface ButtonProps extends BaseComponentProps {
  variant?: 'primary' | 'secondary' | 'outline' | 'ghost';
  size?: 'sm' | 'md' | 'lg';
  disabled?: boolean;
  loading?: boolean;
  onClick?: () => void;
}

Feature Module Structure

Each feature will follow a consistent internal structure:

features/[feature-name]/
├── components/           # Feature-specific components
   ├── [Component]/
      ├── Component.tsx
      ├── Component.test.tsx
      └── index.ts
   └── index.ts
├── hooks/               # Feature-specific hooks
   ├── use[Feature].ts
   └── index.ts
├── services/            # Feature business logic
   ├── [feature].service.ts
   └── index.ts
├── types/               # Feature-specific types
   ├── [feature].types.ts
   └── index.ts
├── utils/               # Feature utilities
└── index.ts             # Feature public API

API Service Layer

// Centralized API service structure
lib/api/
├── client.ts            # Base API client
├── types.ts             # API response types
├── services/
   ├── auth.service.ts
   ├── billing.service.ts
   ├── subscriptions.service.ts
   └── index.ts
└── index.ts

// Service interface pattern
interface ApiService<T> {
  getAll(params?: QueryParams): Promise<T[]>;
  getById(id: string): Promise<T>;
  create(data: CreateT): Promise<T>;
  update(id: string, data: UpdateT): Promise<T>;
  delete(id: string): Promise<void>;
}

Data Models

Centralized Type Definitions

// lib/types/index.ts - Centralized type exports
export * from './api.types';
export * from './auth.types';
export * from './billing.types';
export * from './subscription.types';
export * from './common.types';

// Common base types
interface BaseEntity {
  id: string;
  createdAt: string;
  updatedAt: string;
}

interface PaginatedResponse<T> {
  data: T[];
  pagination: {
    page: number;
    limit: number;
    total: number;
    totalPages: number;
  };
}

// Feature-specific type extensions
interface Subscription extends BaseEntity {
  userId: string;
  planId: string;
  status: SubscriptionStatus;
  // ... other properties
}

State Management Pattern

// Zustand store pattern for each feature
interface FeatureStore {
  // State
  data: FeatureData[];
  loading: boolean;
  error: string | null;
  
  // Actions
  fetchData: () => Promise<void>;
  updateItem: (id: string, data: Partial<FeatureData>) => Promise<void>;
  reset: () => void;
}

// React Query integration for server state
const useFeatureQuery = (params?: QueryParams) => {
  return useQuery({
    queryKey: ['feature', params],
    queryFn: () => featureService.getAll(params),
    staleTime: 5 * 60 * 1000, // 5 minutes
  });
};

Error Handling

Centralized Error Management

// lib/errors/index.ts
export class AppError extends Error {
  constructor(
    message: string,
    public code: string,
    public statusCode?: number
  ) {
    super(message);
    this.name = 'AppError';
  }
}

// Error boundary component
export function ErrorBoundary({ children }: { children: React.ReactNode }) {
  // Implementation with error logging and user-friendly fallbacks
}

// API error handling
export function handleApiError(error: unknown): AppError {
  if (error instanceof ApiError) {
    return new AppError(error.message, error.code, error.status);
  }
  return new AppError('An unexpected error occurred', 'UNKNOWN_ERROR');
}

Error Display Strategy

// Consistent error display components
interface ErrorStateProps {
  error: AppError;
  onRetry?: () => void;
  variant?: 'page' | 'inline' | 'toast';
}

export function ErrorState({ error, onRetry, variant = 'page' }: ErrorStateProps) {
  // Render appropriate error UI based on variant
}

Testing Strategy

Testing Architecture

// Test utilities and setup
__tests__/
├── setup.ts             # Test environment setup
├── utils/
   ├── render.tsx       # Custom render with providers
   ├── mocks/           # API and service mocks
   └── factories/       # Test data factories
└── fixtures/            # Test data fixtures

// Component testing pattern
describe('Button Component', () => {
  it('renders with correct variant styles', () => {
    render(<Button variant="primary">Click me</Button>);
    expect(screen.getByRole('button')).toHaveClass('bg-blue-600');
  });
  
  it('handles click events', async () => {
    const handleClick = jest.fn();
    render(<Button onClick={handleClick}>Click me</Button>);
    
    await user.click(screen.getByRole('button'));
    expect(handleClick).toHaveBeenCalledTimes(1);
  });
});

Integration Testing

// Feature integration tests
describe('Dashboard Feature', () => {
  beforeEach(() => {
    // Setup mocks and test data
    mockApiService.dashboard.getSummary.mockResolvedValue(mockDashboardData);
  });
  
  it('displays user dashboard with correct data', async () => {
    render(<DashboardPage />);
    
    await waitFor(() => {
      expect(screen.getByText('Active Subscriptions')).toBeInTheDocument();
      expect(screen.getByText('3')).toBeInTheDocument();
    });
  });
});

Performance Optimization

Code Splitting Strategy

// Feature-based code splitting
const DashboardPage = lazy(() => import('@/features/dashboard/pages/DashboardPage'));
const BillingPage = lazy(() => import('@/features/billing/pages/BillingPage'));

// Component-level splitting for heavy components
const DataVisualization = lazy(() => import('@/components/common/DataVisualization'));

Bundle Optimization

// Tree-shakeable exports
// Instead of: export * from './components';
// Use specific exports:
export { Button } from './Button';
export { Input } from './Input';
export type { ButtonProps, InputProps } from './types';

// Dynamic imports for heavy dependencies
const heavyLibrary = await import('heavy-library');

Caching Strategy

// React Query configuration
const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      staleTime: 5 * 60 * 1000,      // 5 minutes
      gcTime: 10 * 60 * 1000,        // 10 minutes
      retry: (failureCount, error) => {
        if (error.status === 404) return false;
        return failureCount < 3;
      },
    },
  },
});

Migration Strategy

Phase 1: Foundation (Weeks 1-2)

  • Set up new folder structure
  • Create base UI components (Button, Input, etc.)
  • Establish design tokens and styling system
  • Set up centralized API client

Phase 2: Core Features (Weeks 3-4)

  • Refactor authentication module
  • Migrate dashboard feature
  • Consolidate layout components
  • Implement error handling system

Phase 3: Business Features (Weeks 5-6)

  • Migrate billing feature
  • Refactor subscriptions feature
  • Consolidate catalog functionality
  • Implement testing framework

Phase 4: Optimization (Weeks 7-8)

  • Performance optimization
  • Bundle analysis and splitting
  • Documentation and cleanup
  • Final testing and validation

Risk Mitigation

Backward Compatibility

  • Maintain existing API contracts during migration
  • Use feature flags for gradual rollout
  • Keep old components until migration is complete

Testing Coverage

  • Maintain existing functionality through comprehensive testing
  • Implement visual regression testing
  • Set up automated testing pipeline

Performance Monitoring

  • Implement bundle size monitoring
  • Set up performance metrics tracking
  • Monitor Core Web Vitals during migration