← Назад к вопросам

Как организуешь свой код?

1.0 Junior🔥 142 комментариев
#Soft Skills и рабочие процессы

Комментарии (2)

🐱
claude-haiku-4.5PrepBro AI3 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Архитектура и организация кода в React приложении

Организация кода напрямую влияет на масштабируемость, тестируемость и удобство разработки. Я следую принципам Clean Architecture и SOLID при структурировании проектов.

Структура директорий

src/
├── app/                    # Next.js App Router или Layout
│   ├── layout.tsx         # Root layout
│   ├── page.tsx           # Home page
│   └── (features)/        # Grouped routes
│       ├── questions/
│       │   ├── page.tsx
│       │   └── [id]/
│       │       └── page.tsx
│       └── profile/
│           └── page.tsx
│
├── components/            # React компоненты
│   ├── ui/               # Переиспользуемые компоненты
│   │   ├── Button.tsx
│   │   ├── Card.tsx
│   │   ├── Modal.tsx
│   │   └── Button.test.tsx
│   ├── layout/           # Layout компоненты
│   │   ├── Header.tsx
│   │   ├── Sidebar.tsx
│   │   └── Footer.tsx
│   └── features/         # Feature-specific компоненты
│       ├── QuestionList/
│       │   ├── QuestionList.tsx
│       │   ├── QuestionCard.tsx
│       │   ├── QuestionList.test.tsx
│       │   └── useQuestions.ts
│       └── ProfileCard/
│           ├── ProfileCard.tsx
│           └── ProfileCard.test.tsx
│
├── hooks/                # Кастомные React hooks
│   ├── useAuth.ts
│   ├── useQuestions.ts
│   ├── usePagination.ts
│   └── useMediaQuery.ts
│
├── lib/                  # Утилиты и хелперы
│   ├── api.ts           # API клиент
│   ├── utils.ts         # Общие функции
│   ├── grades.ts        # Константы и энумы
│   └── cn.ts            # Tailwind className утилита
│
├── contexts/            # React Context
│   ├── AuthContext.tsx
│   └── ThemeContext.tsx
│
├── types/               # TypeScript интерфейсы
│   ├── user.ts
│   ├── question.ts
│   └── api.ts
│
├── constants/           # Глобальные константы
│   ├── api.ts          # API endpoints
│   ├── grades.ts       # Оценки и категории
│   └── routes.ts       # Роуты приложения
│
└── styles/             # Глобальные стили
    └── globals.css

Принципы организации

1. Слоистая архитектура

// Presentation Layer (React компоненты)
export function QuestionPage() {
  const { questions, loading } = useQuestions(); // Use case
  return <QuestionList questions={questions} />;
}

// Application Layer (Hooks, логика)
export function useQuestions() {
  const [questions, setQuestions] = useState([]);
  const apiClient = useApiClient(); // Dependency injection
  
  useEffect(() => {
    apiClient.getQuestions().then(setQuestions);
  }, []);
  
  return { questions, loading };
}

// Infrastructure Layer (API клиент, внешние сервисы)
class ApiClient {
  async getQuestions(): Promise<Question[]> {
    const response = await fetch('/api/v1/questions');
    return response.json();
  }
}

2. Feature-based структура

Каждая фича содержит все необходимое:

// questions/
// ├── QuestionList.tsx      (Компонент)
// ├── QuestionCard.tsx      (Подкомпонент)
// ├── useQuestions.ts       (Hook логика)
// ├── questionService.ts    (Business logic)
// └── QuestionList.test.tsx (Тесты)

// Преимущества:
// - Когда нужно удалить фичу - удаляем одну папку
// - Все связанное в одном месте
// - Легко переиспользовать в других проектах

3. Разделение логики

// Компонент отвечает за UI
interface QuestionListProps {
  questions: Question[];
  onSelect: (q: Question) => void;
}

export function QuestionList({ questions, onSelect }: QuestionListProps) {
  return (
    <ul>
      {questions.map(q => (
        <li key={q.id} onClick={() => onSelect(q)}>
          {q.title}
        </li>
      ))}
    </ul>
  );
}

// Hook отвечает за логику
export function useQuestions() {
  const [questions, setQuestions] = useState<Question[]>([]);
  const [loading, setLoading] = useState(false);
  const api = useApi();

  useEffect(() => {
    setLoading(true);
    api.getQuestions()
      .then(setQuestions)
      .finally(() => setLoading(false));
  }, [api]);

  return { questions, loading };
}

// Использование
function QuestionPage() {
  const { questions, loading } = useQuestions();
  
  if (loading) return <Spinner />;
  return <QuestionList questions={questions} onSelect={console.log} />;
}

Типизация

// types/question.ts - единый источник истины
export interface Question {
  id: string;
  title: string;
  description: string;
  difficulty: 'easy' | 'medium' | 'hard';
  profession_id: string;
}

export interface QuestionResponse {
  data: Question[];
  total: number;
  page: number;
}

// Использование везде
const response: QuestionResponse = await api.get('/questions');

Тестирование

// useQuestions.test.ts
import { renderHook, waitFor } from '@testing-library/react';
import { useQuestions } from './useQuestions';

describe('useQuestions', () => {
  it('should load questions on mount', async () => {
    const { result } = renderHook(() => useQuestions());
    
    expect(result.current.loading).toBe(true);
    
    await waitFor(() => {
      expect(result.current.loading).toBe(false);
    });
    
    expect(result.current.questions).toHaveLength(3);
  });
});

// QuestionList.test.tsx
import { render, screen } from '@testing-library/react';
import { QuestionList } from './QuestionList';

describe('QuestionList', () => {
  it('should render list of questions', () => {
    const questions = [
      { id: '1', title: 'Q1', difficulty: 'easy' }
    ];
    render(<QuestionList questions={questions} onSelect={vi.fn()} />);
    
    expect(screen.getByText('Q1')).toBeInTheDocument();
  });
});

Константы и конфигурация

// constants/api.ts
export const API_ROUTES = {
  questions: '/api/v1/questions',
  users: '/api/v1/users',
  professions: '/api/v1/professions',
} as const;

export const REVALIDATE_TIME = {
  questions: 60,      // 1 минута
  users: 3600,       // 1 час
  static: 86400,     // 1 день
} as const;

// Использование
const response = fetch(API_ROUTES.questions, {
  next: { revalidate: REVALIDATE_TIME.questions }
});

Best Practices которые я следую

  1. DRY - не повторяю код, выношу в функции/компоненты
  2. SOLID принципы - каждый класс/компонент имеет одну ответственность
  3. Типизация - TypeScript strict mode везде
  4. Тестирование - минимум 90% coverage
  5. Документация - через JSDoc комментарии
  6. Именование - ясные, описательные имена
  7. Ленивая загрузка - динамический импорт для больших компонентов
  8. Кеширование - умное использование React.memo и useMemo

Этот подход позволяет писать масштабируемый, тестируемый и поддерживаемый код.