react-spa-template/docs/project-structure.md
2025-04-19 19:24:27 +02:00

141 lines
3.7 KiB
Markdown

# Project Structure
This document outlines the structure of our application, which follows a domain-driven, Flux-style architecture using Effector for state management.
## Directory Structure
```
src/
app/ # Application entry point and configuration
root.tsx # Root component
routes.tsx # Route definitions
domains/ # Feature domains
<feature>/ # e.g., products, auth, orders
model.ts # Effector events, stores, effects
view-model.ts # React hooks for connecting to the model
ui/ # React components
index.ts # Exports all components
Component.tsx # Individual components
shared/ # Shared code
lib/ # Utilities, helpers, adapters
ui/ # Reusable UI components
tests/ # Test files
```
## Domain Structure
Each domain follows a consistent structure:
### model.ts
Contains all Effector-related code:
- Events (actions)
- Stores (state)
- Effects (async operations)
Example:
```typescript
// domains/products/model.ts
import { createStore, createEvent, createEffect } from 'effector';
import { ProductsViewsService } from '@lib/api/merchant/services/ProductsViewsService';
import type { productCompositeDto } from '@lib/api/merchant/models/productCompositeDto';
// Events
export const setProductsFilter = createEvent<Partial<productQueryRequestDto>>();
// Effects
export const fetchProductsFx = createEffect(async (filter: productQueryRequestDto) => {
return await ProductsViewsService.postApiProductCompositeQueryByCanonical({
requestBody: filter
});
});
// Stores
export const $products = createStore<productCompositeDto[]>([])
.on(fetchProductsFx.doneData, (_, payload) => Array.isArray(payload) ? payload : [payload]);
```
### view-model.ts
Contains React hooks that connect to the Effector model:
```typescript
// domains/products/view-model.ts
import { useStore } from 'effector-react';
import { useCallback } from 'react';
import {
$products,
$productsLoading,
setProductsFilter,
fetchProductsFx
} from './model';
export const useProducts = () => {
const products = useStore($products);
const isLoading = useStore($productsLoading);
const updateFilter = useCallback((newFilter) => {
setProductsFilter(newFilter);
}, []);
return {
products,
isLoading,
updateFilter
};
};
```
### ui/
Contains React components that use the view-model:
```typescript
// domains/products/ui/ProductsList.tsx
import React from 'react';
import { useProducts } from '../view-model';
import { ProductCard } from './ProductCard';
export const ProductsList: React.FC = () => {
const { products, isLoading } = useProducts();
if (isLoading) {
return <div>Loading...</div>;
}
return (
<div className="grid grid-cols-3 gap-4">
{products.map(product => (
<ProductCard key={product.id} product={product} />
))}
</div>
);
};
```
## Shared Code
### shared/lib/
Contains utilities, helpers, and adapters that are used across multiple domains.
### shared/ui/
Contains reusable UI components that are not tied to any specific domain.
## Tests
Tests are organized alongside the code they test, following the same structure.
## Best Practices
1. **Keep domains isolated**: Each domain should be self-contained and not depend on other domains.
2. **Use named exports**: Avoid default exports to make refactoring easier.
3. **Keep files small**: Each file should have a single responsibility.
4. **Use TypeScript**: All files should be written in TypeScript for type safety.
5. **Follow Flux pattern**: Maintain unidirectional data flow from events to stores to views.