448 lines
14 KiB
Markdown
448 lines
14 KiB
Markdown
# Architecture Overview & Design Patterns
|
|
|
|
## System Architecture
|
|
|
|
### High-Level Architecture
|
|
```
|
|
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
|
|
│ Browser │ │ Backend │ │ External │
|
|
│ (Frontend) │ │ Services │ │ Services │
|
|
│ │ │ │ │ │
|
|
│ ┌─────────────┐ │ │ ┌─────────────┐ │ │ ┌─────────────┐ │
|
|
│ │ React App │◄┼────┼──│ Express API │◄┼────┼──│ GLM-4.6 API │ │
|
|
│ │ (Vite) │ │ │ │ (EPUB Gen) │ │ │ │ (Translation)│ │
|
|
│ └─────────────┘ │ │ └─────────────┘ │ │ └─────────────┘ │
|
|
│ │ │ │ │ │
|
|
│ ┌─────────────┐ │ │ │ │ ┌─────────────┐ │
|
|
│ │ Supabase │◄┼────────────────────┼────┼──│ Supabase │ │
|
|
│ │ Auth │ │ │ │ │ Database │ │
|
|
│ └─────────────┘ │ │ │ └─────────────┘ │
|
|
│ │ │ │ │
|
|
│ ┌─────────────┐ │ │ │ │
|
|
│ │ Tesseract │ │ │ │ │
|
|
│ │ OCR Engine │ │ │ │ │
|
|
│ └─────────────┘ │ │ │ │
|
|
└─────────────────┘ └─────────────────┘ └─────────────────┘
|
|
```
|
|
|
|
### Component Architecture
|
|
|
|
#### Frontend Layer Architecture
|
|
```
|
|
┌─────────────────────────────────────────────────────────────┐
|
|
│ Presentation Layer │
|
|
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────────┐ │
|
|
│ │ Header │ │ Stepper │ │ Main Content │ │
|
|
│ │ Component │ │ Component │ │ (Router Outlet) │ │
|
|
│ └─────────────┘ └─────────────┘ └─────────────────────────┘ │
|
|
└─────────────────────────────────────────────────────────────┘
|
|
│
|
|
┌─────────────────────────────────────────────────────────────┐
|
|
│ Application Layer │
|
|
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────────┐ │
|
|
│ │ Router │ │ State │ │ Utility │ │
|
|
│ │ Management │ │ Management │ │ Services │ │
|
|
│ │(React Router)│ │ (Zustand) │ │ (API, Auth, Utils) │ │
|
|
│ └─────────────┘ └─────────────┘ └─────────────────────────┘ │
|
|
└─────────────────────────────────────────────────────────────┘
|
|
│
|
|
┌─────────────────────────────────────────────────────────────┐
|
|
│ Data Layer │
|
|
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────────┐ │
|
|
│ │ Browser │ │ Local │ │ External │ │
|
|
│ │ Storage │ │ Storage │ │ APIs │ │
|
|
│ │ (Blobs, URLs)│ │ (localStorage)│ │ (Supabase, Backend) │ │
|
|
│ └─────────────┘ └─────────────┘ └─────────────────────────┘ │
|
|
└─────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
## Design Patterns
|
|
|
|
### 1. State Management Pattern (Zustand)
|
|
```javascript
|
|
// Centralized Store Pattern
|
|
const useAppStore = create((set, get) => ({
|
|
// State
|
|
uploadedImages: [],
|
|
cropConfig: {},
|
|
ocrText: '',
|
|
|
|
// Actions
|
|
setUploadedImages: (images) => set({ uploadedImages: images }),
|
|
resetFromStep: (step) => set((state) => {
|
|
// Clean, atomic state updates
|
|
const newState = {};
|
|
// Reset logic based on step
|
|
return newState;
|
|
}),
|
|
}));
|
|
|
|
// Usage in components
|
|
const uploadedImages = useAppStore(state => state.uploadedImages);
|
|
const setUploadedImages = useAppStore(state => state.setUploadedImages);
|
|
```
|
|
|
|
### 2. Component Composition Pattern
|
|
```javascript
|
|
// Wizard Step Composition
|
|
const WizardStep = ({ children, title, onNext, onPrev }) => {
|
|
return (
|
|
<Box>
|
|
<Typography variant="h6">{title}</Typography>
|
|
{children}
|
|
<StepActions onNext={onNext} onPrev={onPrev} />
|
|
</Box>
|
|
);
|
|
};
|
|
|
|
// Specific step implementation
|
|
const UploadStep = () => {
|
|
return (
|
|
<WizardStep title="Upload Images">
|
|
<DropzoneUpload />
|
|
<ImagePreview />
|
|
</WizardStep>
|
|
);
|
|
};
|
|
```
|
|
|
|
### 3. Service Layer Pattern
|
|
```javascript
|
|
// API Service Abstraction
|
|
export const epubService = {
|
|
generate: async (text, metadata) => {
|
|
const response = await fetch(`${API_BASE_URL}/generate-epub`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ text, meta: metadata }),
|
|
});
|
|
return response.json();
|
|
},
|
|
};
|
|
|
|
// Authentication Service
|
|
export const authService = {
|
|
login: async (credentials) => {
|
|
// Authentication logic
|
|
},
|
|
logout: async () => {
|
|
// Logout logic
|
|
},
|
|
};
|
|
```
|
|
|
|
### 4. Observer Pattern (Authentication)
|
|
```javascript
|
|
// Supabase Auth Observer
|
|
useEffect(() => {
|
|
const { data: subscription } = supabaseClient.auth.onAuthStateChange(
|
|
(event, session) => {
|
|
if (event === 'SIGNED_IN') {
|
|
// Handle sign in
|
|
exchangeGoogleSession(session);
|
|
} else if (event === 'SIGNED_OUT') {
|
|
// Handle sign out
|
|
clearAuthSession();
|
|
}
|
|
}
|
|
);
|
|
|
|
return () => subscription.subscription.unsubscribe();
|
|
}, []);
|
|
```
|
|
|
|
### 5. Strategy Pattern (OCR Processing)
|
|
```javascript
|
|
// OCR Language Strategy
|
|
const ocrLanguageStrategy = {
|
|
primary: 'tur',
|
|
fallback: 'eng',
|
|
detect: async (imageData) => {
|
|
try {
|
|
const result = await worker.recognize(imageData, 'tur');
|
|
return result.data.text;
|
|
} catch (error) {
|
|
// Fallback to English
|
|
return await worker.recognize(imageData, 'eng');
|
|
}
|
|
},
|
|
};
|
|
```
|
|
|
|
## Data Flow Architecture
|
|
|
|
### Unidirectional Data Flow
|
|
```
|
|
User Action → Component Event → State Update → Re-render → New UI
|
|
|
|
Example:
|
|
1. User uploads files
|
|
2. Dropzone component triggers onDrop
|
|
3. useAppStore.setUploadedImages() called
|
|
4. State updated in Zustand store
|
|
5. Components subscribed to state re-render
|
|
6. New UI shows uploaded images
|
|
```
|
|
|
|
### Async Data Handling
|
|
```javascript
|
|
// Async Action Pattern
|
|
const handleOcrProcessing = async () => {
|
|
try {
|
|
setTranslationStatus('processing');
|
|
setTranslationProgress(0);
|
|
|
|
const processedText = await processOcrImages(
|
|
croppedImages,
|
|
(progress) => setTranslationProgress(progress)
|
|
);
|
|
|
|
setOcrText(processedText);
|
|
setTranslationStatus('completed');
|
|
} catch (error) {
|
|
setTranslationError(error.message);
|
|
setTranslationStatus('error');
|
|
}
|
|
};
|
|
```
|
|
|
|
## Component Architecture
|
|
|
|
### Hierarchy Structure
|
|
```
|
|
App (Root Component)
|
|
├── Header (Authentication & Navigation)
|
|
├── Stepper (Wizard Progress)
|
|
├── Main Content (Router Outlet)
|
|
│ ├── UploadStep
|
|
│ ├── CropStep
|
|
│ ├── BulkCropStep
|
|
│ ├── OcrStep
|
|
│ ├── TranslationStep
|
|
│ ├── EpubStep
|
|
│ ├── DownloadStep
|
|
│ └── Auth Pages
|
|
└── Error Handling (Snackbar)
|
|
```
|
|
|
|
### Smart vs Dumb Components
|
|
```javascript
|
|
// Smart Component (Stateful)
|
|
const OcrStep = () => {
|
|
const { croppedImages, ocrText, setOcrText } = useAppStore();
|
|
const [isProcessing, setIsProcessing] = useState(false);
|
|
|
|
const handleOcrStart = async () => {
|
|
setIsProcessing(true);
|
|
// OCR processing logic
|
|
setOcrText(resultText);
|
|
setIsProcessing(false);
|
|
};
|
|
|
|
return (
|
|
<OcrProcessing
|
|
images={croppedImages}
|
|
onStart={handleOcrStart}
|
|
isProcessing={isProcessing}
|
|
result={ocrText}
|
|
/>
|
|
);
|
|
};
|
|
|
|
// Dumb Component (Presentational)
|
|
const OcrProcessing = ({ images, onStart, isProcessing, result }) => {
|
|
return (
|
|
<Box>
|
|
<Button onClick={onStart} disabled={isProcessing}>
|
|
Start OCR
|
|
</Button>
|
|
{isProcessing && <ProgressIndicator />}
|
|
{result && <TextResult text={result} />}
|
|
</Box>
|
|
);
|
|
};
|
|
```
|
|
|
|
## Security Architecture
|
|
|
|
### Authentication Flow
|
|
```
|
|
User Login Attempt
|
|
↓
|
|
Supabase Auth Service
|
|
↓
|
|
JWT Token Generation
|
|
↓
|
|
Token Storage (localStorage)
|
|
↓
|
|
Authenticated API Requests
|
|
↓
|
|
Token Validation & Refresh
|
|
```
|
|
|
|
### Data Security Measures
|
|
```javascript
|
|
// Secure API Requests
|
|
const secureApiCall = async (endpoint, data) => {
|
|
const token = useAppStore.getState().authToken;
|
|
|
|
return await fetch(endpoint, {
|
|
headers: {
|
|
'Authorization': `Bearer ${token}`,
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify(data),
|
|
});
|
|
};
|
|
|
|
// Data Sanitization
|
|
const sanitizeInput = (input) => {
|
|
return input
|
|
.trim()
|
|
.replace(/[<>]/g, '') // Remove potential HTML
|
|
.substring(0, MAX_LENGTH); // Length limit
|
|
};
|
|
```
|
|
|
|
## Performance Architecture
|
|
|
|
### Lazy Loading Pattern
|
|
```javascript
|
|
// Code Splitting
|
|
const UploadStep = lazy(() => import('./components/UploadStep'));
|
|
const CropStep = lazy(() => import('./components/CropStep'));
|
|
|
|
// Asset Lazy Loading
|
|
const loadTesseractAssets = async () => {
|
|
const worker = await createWorker('tur', 1, {
|
|
workerPath: '/tesseract/worker.min.js',
|
|
corePath: '/tesseract/tesseract-core-simd-lstm.wasm.js',
|
|
langPath: '/tesseract/',
|
|
});
|
|
return worker;
|
|
};
|
|
```
|
|
|
|
### Memory Management Pattern
|
|
```javascript
|
|
// Resource Cleanup
|
|
useEffect(() => {
|
|
return () => {
|
|
// Cleanup on unmount
|
|
if (generatedEpub?.url) {
|
|
URL.revokeObjectURL(generatedEpub.url);
|
|
}
|
|
};
|
|
}, []);
|
|
|
|
// Batch Processing Control
|
|
const processImagesInBatches = async (images, batchSize = 5) => {
|
|
for (let i = 0; i < images.length; i += batchSize) {
|
|
const batch = images.slice(i, i + batchSize);
|
|
await processBatch(batch);
|
|
|
|
// Allow UI to breathe
|
|
await new Promise(resolve => setTimeout(resolve, 100));
|
|
}
|
|
};
|
|
```
|
|
|
|
## Error Handling Architecture
|
|
|
|
### Error Boundary Pattern
|
|
```javascript
|
|
const ErrorBoundary = ({ children }) => {
|
|
const [hasError, setHasError] = useState(false);
|
|
const [error, setError] = useState(null);
|
|
|
|
const handleError = (error, errorInfo) => {
|
|
setHasError(true);
|
|
setError(error);
|
|
// Log error to service
|
|
logError(error, errorInfo);
|
|
};
|
|
|
|
if (hasError) {
|
|
return <ErrorFallback error={error} />;
|
|
}
|
|
|
|
return (
|
|
<ErrorBoundary onError={handleError}>
|
|
{children}
|
|
</ErrorBoundary>
|
|
);
|
|
};
|
|
```
|
|
|
|
### Global Error Handling
|
|
```javascript
|
|
// Global Error Store
|
|
const useErrorStore = create((set) => ({
|
|
error: null,
|
|
setError: (message) => set({ error: message }),
|
|
clearError: () => set({ error: null }),
|
|
}));
|
|
|
|
// Error Display Component
|
|
const ErrorDisplay = () => {
|
|
const error = useAppStore(state => state.error);
|
|
const clearError = useAppStore(state => state.clearError);
|
|
|
|
return (
|
|
<Snackbar open={!!error} autoHideDuration={6000} onClose={clearError}>
|
|
<Alert severity="error" onClose={clearError}>
|
|
{error}
|
|
</Alert>
|
|
</Snackbar>
|
|
);
|
|
};
|
|
```
|
|
|
|
## Testing Architecture
|
|
|
|
### Component Testing Strategy
|
|
```
|
|
Unit Tests:
|
|
├── Utility functions (fileUtils, ocrUtils)
|
|
├── Service layer functions
|
|
└── Custom hooks
|
|
|
|
Integration Tests:
|
|
├── Component interactions
|
|
├── State management flow
|
|
└── API service integration
|
|
|
|
E2E Tests:
|
|
├── Complete user workflows
|
|
├── Authentication flows
|
|
└── File processing pipelines
|
|
```
|
|
|
|
## Deployment Architecture
|
|
|
|
### Build Process
|
|
```
|
|
Development:
|
|
├── Vite dev server
|
|
├── Hot module replacement
|
|
├── Source maps
|
|
└── Fast refresh
|
|
|
|
Production:
|
|
├── Code optimization
|
|
├── Asset bundling
|
|
├── Tree shaking
|
|
└── Compression
|
|
```
|
|
|
|
### Container Strategy
|
|
```dockerfile
|
|
# Multi-stage Docker build
|
|
FROM node:18-alpine AS builder
|
|
# Build stage
|
|
|
|
FROM nginx:alpine AS production
|
|
# Production stage with static files
|
|
```
|
|
|
|
This architecture provides a robust, scalable foundation for the imgPub application with clear separation of concerns, maintainable code structure, and optimized performance characteristics. |