3.7 KiB
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:
// 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:
// 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:
// 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
-
Keep domains isolated: Each domain should be self-contained and not depend on other domains.
-
Use named exports: Avoid default exports to make refactoring easier.
-
Keep files small: Each file should have a single responsibility.
-
Use TypeScript: All files should be written in TypeScript for type safety.
-
Follow Flux pattern: Maintain unidirectional data flow from events to stores to views.