396 lines
10 KiB
Markdown
396 lines
10 KiB
Markdown
|
|
# 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
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// 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
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// 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:
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
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
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// 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
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// 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
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// 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
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// 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
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// 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
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// 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
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// 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
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// 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
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// 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
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// 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
|