12 KiB
12 KiB
Data Flow Diagrams
1. Game Browse and Search Flow
sequenceDiagram
participant User
participant Browser
participant BrowseView
participant useGames Hook
participant API Client
participant Backend
participant GameService
participant FlashpointDB
User->>Browser: Navigate to /browse
Browser->>BrowseView: Render component
BrowseView->>useGames Hook: Initialize with filters
useGames Hook->>API Client: gamesApi.search(filters)
API Client->>Backend: GET /api/games?page=1&limit=50&platform=Flash
Backend->>GameService: searchGames(query)
GameService->>FlashpointDB: SELECT * FROM game WHERE...
FlashpointDB-->>GameService: Game rows
GameService-->>Backend: PaginatedResult<Game>
Backend-->>API Client: JSON response
API Client->>useGames Hook: Update cache
useGames Hook->>BrowseView: Trigger re-render
BrowseView->>Browser: Display game grid
User->>Browser: Select platform "HTML5"
Browser->>BrowseView: Update filters state
BrowseView->>useGames Hook: Refetch with new filters
useGames Hook->>API Client: gamesApi.search(newFilters)
API Client->>Backend: GET /api/games?platform=HTML5
Backend-->>API Client: Filtered results
API Client->>useGames Hook: Update cache
useGames Hook->>BrowseView: Trigger re-render
BrowseView->>Browser: Display filtered games
Data Transformation:
- Frontend state:
{ search: "mario", platform: "Flash", page: 1, limit: 50 } - URL params:
?search=mario&platform=Flash&page=1&limit=50 - Backend validation: Zod schema validation
- SQL:
SELECT * FROM game WHERE platformName = 'Flash' AND title LIKE '%mario%' ... LIMIT 50 OFFSET 0
2. Game Detail View Flow
sequenceDiagram
participant User
participant Browser
participant GameDetailView
participant useGame Hook
participant useRelated Hook
participant API Client
participant Backend
participant FlashpointDB
User->>Browser: Click on game card
Browser->>GameDetailView: Navigate to /games/:id
GameDetailView->>useGame Hook: useQuery(['game', id])
GameDetailView->>useRelated Hook: useQuery(['related', id])
par Parallel Requests
useGame Hook->>API Client: gamesApi.getById(id)
API Client->>Backend: GET /api/games/{id}
Backend->>FlashpointDB: SELECT * FROM game
FlashpointDB-->>Backend: Game row
Backend-->>API Client: Game object
API Client->>useGame Hook: Cache game data
and
useRelated Hook->>API Client: gamesApi.getRelated(id)
API Client->>Backend: GET /api/games/{id}/related?limit=10
Backend->>FlashpointDB: SELECT related by developer/platform
FlashpointDB-->>Backend: Related games
Backend-->>API Client: Game array
API Client->>useRelated Hook: Cache related games
end
useGame Hook->>GameDetailView: Trigger render
useRelated Hook->>GameDetailView: Trigger render
GameDetailView->>Browser: Display game details
3. Filter Options Loading Flow
sequenceDiagram
participant User
participant Browser
participant FilterPanel
participant useFilterOptions Hook
participant API Client
participant Backend
participant FlashpointDB
User->>Browser: Click "Filters" button
Browser->>FilterPanel: Open filter panel
FilterPanel->>useFilterOptions Hook: useQuery(['filterOptions'])
Note over useFilterOptions Hook: Check cache first
alt Cache Hit
useFilterOptions Hook->>FilterPanel: Return cached data
else Cache Miss
useFilterOptions Hook->>API Client: gamesApi.getFilterOptions()
API Client->>Backend: GET /api/games/filter-options
Backend->>Backend: Parallel queries
par All Filter Options
Backend->>FlashpointDB: SELECT DISTINCT platformName, COUNT(*)
FlashpointDB-->>Backend: Platforms
and
Backend->>FlashpointDB: SELECT DISTINCT developer, COUNT(*)
FlashpointDB-->>Backend: Developers
and
Backend->>FlashpointDB: SELECT DISTINCT tag, COUNT(*)
FlashpointDB-->>Backend: Tags
and
Backend->>FlashpointDB: SELECT MIN/MAX year
FlashpointDB-->>Backend: Year range
end
Backend-->>API Client: FilterOptions object
API Client->>useFilterOptions Hook: Cache with infinite staleTime
useFilterOptions Hook->>FilterPanel: Render
end
FilterPanel->>Browser: Display filters
Filter Options Response:
{
"platforms": [
{ "name": "Flash", "count": 85000 },
{ "name": "HTML5", "count": 15000 }
],
"developers": [{ "name": "Developer A", "count": 500 }],
"tags": [{ "name": "Action", "count": 15000 }],
"yearRange": { "min": 1995, "max": 2024 }
}
4. Image Loading Flow
sequenceDiagram
participant Browser
participant GameCard
participant LazyImage
participant Backend
participant FileSystem
Browser->>GameCard: Render component
GameCard->>LazyImage: <img src="/api/images/logo/{gameId}" />
Note over LazyImage: Uses Intersection Observer
alt Image in viewport
LazyImage->>Backend: GET /api/images/logo/{gameId}
Backend->>FileSystem: fs.readFileSync(logoPath)
alt File exists
FileSystem-->>Backend: Image bytes
Backend-->>LazyImage: Image with Cache-Control headers
LazyImage->>Browser: Display image
else File not found
Backend-->>LazyImage: 404 Not Found
LazyImage->>Browser: Display fallback
end
else Image not in viewport
Note over LazyImage: Wait for scroll
end
5. Pagination Flow
sequenceDiagram
participant User
participant Browser
participant BrowseView
participant Pagination
participant useGames Hook
participant API Client
participant Backend
User->>Browser: Click "Next Page"
Browser->>Pagination: onClick handler
Pagination->>BrowseView: Update page state
BrowseView->>BrowseView: Update URL: ?page=2
BrowseView->>useGames Hook: Filters changed (page=2)
alt Page 2 in cache
useGames Hook->>BrowseView: Return cached page 2
else Page 2 not cached
useGames Hook->>API Client: gamesApi.search({...filters, page: 2})
API Client->>Backend: GET /api/games?page=2&limit=50
Backend->>Backend: Calculate offset: (2-1) * 50 = 50
Backend-->>API Client: Page 2 data
API Client->>useGames Hook: Cache page 2
useGames Hook->>BrowseView: Trigger render
end
BrowseView->>Browser: Display games
Browser->>User: Show page 2
6. Real-time Search Flow
sequenceDiagram
participant User
participant Browser
participant SearchBar
participant Debouncer
participant BrowseView
participant useGames Hook
participant API Client
participant Backend
User->>Browser: Type "m"
Browser->>SearchBar: onChange event
SearchBar->>Debouncer: Schedule update (300ms)
User->>Browser: Type "a"
Browser->>SearchBar: onChange event
SearchBar->>Debouncer: Cancel previous, schedule new (300ms)
User->>Browser: Type "r"
Browser->>SearchBar: onChange event
SearchBar->>Debouncer: Cancel previous, schedule new (300ms)
Note over Debouncer: 300ms elapsed
Debouncer->>BrowseView: Update filters: {search: "mar"}
BrowseView->>useGames Hook: Refetch with search
useGames Hook->>API Client: gamesApi.search({search: "mar"})
API Client->>Backend: GET /api/games?search=mar
Backend-->>API Client: Filtered results
API Client->>useGames Hook: Update cache
useGames Hook->>BrowseView: Trigger render
BrowseView->>Browser: Display search results
Debounce Implementation:
const debouncedSearch = useMemo(
() =>
debounce((value: string) => {
onSearch(value);
}, 300),
[onSearch]
);
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const value = e.target.value;
setSearchValue(value); // Immediate UI update
debouncedSearch(value); // Debounced API call
};
7. Multi-Select Filter Flow
sequenceDiagram
participant User
participant Browser
participant FilterPanel
participant BrowseView
participant API Client
participant Backend
User->>Browser: Check "Flash" checkbox
Browser->>FilterPanel: Update selected: ["Flash"]
FilterPanel->>BrowseView: Update filters
BrowseView->>API Client: GET /api/games?platform=Flash
API Client->>Backend: Request with single platform
Backend-->>Browser: Show Flash games
User->>Browser: Check "HTML5" checkbox
Browser->>FilterPanel: Update selected: ["Flash", "HTML5"]
FilterPanel->>BrowseView: Update filters
BrowseView->>API Client: GET /api/games?platform=Flash,HTML5
API Client->>Backend: Request with comma-separated platforms
Backend-->>Browser: Show Flash OR HTML5 games
Multi-Select SQL:
// Frontend: Array to string
const platformParam = selectedPlatforms.join(','); // "Flash,HTML5"
// Backend: Parse back
const platforms = query.platform?.split(',').filter(Boolean); // ["Flash", "HTML5"]
// SQL: Dynamic placeholders
if (platforms?.length > 0) {
const placeholders = platforms.map(() => '?').join(', ');
sql += ` AND g.platformName IN (${placeholders})`;
params.push(...platforms);
}
8. Sort Order Change Flow
sequenceDiagram
participant User
participant Browser
participant SortControl
participant BrowseView
participant API Client
participant Backend
User->>Browser: Click "Release Date" sort
Browser->>SortControl: Update sortBy
SortControl->>BrowseView: Update filters
BrowseView->>API Client: GET /api/games?sortBy=releaseDate&sortOrder=asc
API Client->>Backend: Request with new sort
Backend-->>Browser: Games sorted by release date
User->>Browser: Click "Release Date" again (toggle)
Browser->>SortControl: Toggle sortOrder
SortControl->>BrowseView: Update filters
BrowseView->>API Client: GET /api/games?sortBy=releaseDate&sortOrder=desc
API Client->>Backend: Request with reversed order
Backend-->>Browser: Newest games first
9. Cache Management
graph TB
UserAction[User Action]
CheckCache{Cache exists?}
CheckStale{Is stale?}
ReturnCache[Return from cache]
FetchBackground[Fetch in background]
FetchForeground[Fetch immediately]
UpdateCache[Update cache]
RenderUI[Render UI]
UserAction --> CheckCache
CheckCache -->|Yes| CheckStale
CheckCache -->|No| FetchForeground
CheckStale -->|No| ReturnCache
CheckStale -->|Yes| FetchBackground
ReturnCache --> RenderUI
ReturnCache -.-> FetchBackground
FetchBackground --> UpdateCache
FetchForeground --> UpdateCache
UpdateCache --> RenderUI
style ReturnCache fill:#90ee90
style FetchForeground fill:#ffcccb
style FetchBackground fill:#ffd700
Stale-While-Revalidate:
useQuery({
queryKey: ['games', filters],
queryFn: () => gamesApi.search(filters),
staleTime: 5 * 60 * 1000, // 5 min - fresh
cacheTime: 10 * 60 * 1000, // 10 min - in memory
refetchOnWindowFocus: true, // Refresh on tab focus
keepPreviousData: true, // Show old while fetching new
});
Key Patterns
- Layered Architecture: UI → API → Service → Database
- Smart Caching: TanStack Query provides automatic cache management
- Optimistic Updates: UI updates immediately with rollback on error
- Parallel Requests: Multiple queries execute concurrently
- Debouncing: User input debounced to reduce requests
- State Synchronization: URL state, local state, and server state in sync