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

Почему не нужно применять принципы SOLID?

2.2 Middle🔥 191 комментариев
#JavaScript Core

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

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

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

Почему не нужно применять принципы SOLID?

Это интересный вопрос. На самом деле, SOLID принципы НЕ всегда плохие - но их часто неправильно применяют, особенно в frontend разработке. Давайте разберемся, когда SOLID становится вредоносным.

Когда SOLID приносит вред

1. Over-engineering (Оверинжиниринг)

Проблема: Разработчик создает абстракции "на будущее", которые никогда не используются.

// ПЛОХО - over-engineering
interface UserRepository {
  getUser(id: string): Promise<User>;
  saveUser(user: User): Promise<void>;
  deleteUser(id: string): Promise<void>;
}

class PostgresUserRepository implements UserRepository {
  // ...
}

class MockUserRepository implements UserRepository {
  // ...
}

// Затем тонна код-боилеркоада просто для примерно равного хранилища

Что происходит:

  • Код становится сложнее
  • Разработчики тратят время на абстракции
  • Сложнее понять логику (нужно прыгать между файлами)
  • Абстракции часто не нужны (никогда не меняются)

Когда это реально нужно: Только если у вас РЕАЛЬНО есть несколько реализаций или планы их добавить.

2. Множество маленьких файлов

Проблема: SOLID требует разделения ответственности, но это приводит к паралич паралляизма.

// ПЛОХО - too much separation
// src/components/User/
//   - UserContainer.tsx
//   - UserPresenter.tsx
//   - UserViewModel.ts
//   - useUserFetch.ts
//   - UserTypes.ts
//   - UserUtils.ts
//   - UserStyles.ts
//   - __tests__/

// Для простого компонента из 50 строк создали 7 файлов!

Проблемы:

  • Когда что-то сломалось, нужно исправить везде
  • Сложно найти, где нужный код
  • Время на навигацию и редактирование увеличивается
  • Pull request'ы огромные

3. DI (Dependency Injection) в React

Проблема: Inject dependencies через функции приводит к паралич на клиенте.

// ПЛОХО - DI в React
interface ApiService {
  getUsers(): Promise<User[]>;
}

interface UserListProps {
  apiService: ApiService;
}

const UserList = ({ apiService }: UserListProps) => {
  const [users, setUsers] = useState<User[]>([]);
  
  useEffect(() => {
    apiService.getUsers().then(setUsers);
  }, [apiService]);
  
  return <div>{users.map(u => <div>{u.name}</div>)}</div>;
};

// Использование требует
<UserList apiService={new ApiService()} />

// Или даже хуже - глобальный контекст
const ApiContext = createContext<ApiService | null>(null);

const UserList = () => {
  const apiService = useContext(ApiContext);
  if (!apiService) throw new Error('Missing provider');
  // ...
};

Почему это плохо в React:

  • React уже имеет встроенный DI через props
  • Context для этого слишком мощен
  • Сложнее тестировать (нужны провайдеры)
  • Код становится более имплицитным

4. Принцип Open/Closed vs Простота

Проблема: Делаешь код открытым для расширения, но это усложняет текущий код.

// ПЛОХО - OCP over-engineering
interface Theme {
  applyButton(): string;
  applyInput(): string;
  applyHeader(): string;
  // + 20 других методов
}

class DarkTheme implements Theme { /* ... */ }
class LightTheme implements Theme { /* ... */ }

// Использование
const applyTheme = (theme: Theme) => {
  // Нужно знать все методы Theme интерфейса
};

Зачем это нужно? Если есть больше 2-3 тем. Иначе это просто overkill.

Почему SOLID часто вредит в frontend

Причина 1: Frontend часто меняется

Жизненный цикл backend'а:
В 2020 году создали API -> работает с небольшими изменениями до 2024
Абстракции приносят пользу

Жизненный цикл frontend'а:
В 2024 году переписали на React -> 2024 переписали на Vue -> 2025 переписали на Svelte
Абстракции устаревают

Причина 2: React уже хорошо решает많은 проблем

// React уже дает инверсию управления через JSX
<UserList 
  users={users}
  onUserClick={handleUserClick}
  renderItem={item => <CustomItem {...item} />}
/>

// Нужна ли еще одна слой DI?

Причина 3: Тестирование в React проще, чем в backend

// Jest + React Testing Library - очень просто тестировать
test('UserList shows users', () => {
  render(<UserList />);
  expect(screen.getByText('John')).toBeInTheDocument();
});

// Не нужны моки и сложные DI контейнеры

Когда SOLID приносит пользу

1. Большой проект (50+ компонентов)

// Single Responsibility имеет смысл
const UserCard = ({ user }) => <div>{user.name}</div>;
const UserList = ({ users }) => users.map(u => <UserCard user={u} />);
const UserPage = () => {
  const [users, setUsers] = useState([]);
  // ...
  return <UserList users={users} />;
};

// Каждый компонент - одна ответственность
// Легко найти и изменить

2. Общие утилиты

// DRY + SOLID имеет смысл для утилит
const formatDate = (date: Date): string => date.toLocaleDateString();
const formatCurrency = (amount: number): string => `$${amount.toFixed(2)}`;
const formatTime = (time: Date): string => time.toLocaleTimeString();

// Переиспользуется везде
import { formatDate, formatCurrency } from '@/lib/formatters';

3. Множество реализаций

// Interface имеет смысл, если РЕАЛЬНО есть 2+ реализации
interface HttpClient {
  get<T>(url: string): Promise<T>;
  post<T>(url: string, data: unknown): Promise<T>;
}

class FetchHttpClient implements HttpClient { /* ... */ }
class AxiosHttpClient implements HttpClient { /* ... */ }
class MockHttpClient implements HttpClient { /* ... */ }

// Тестирование
const httpClient = new MockHttpClient();
const userService = new UserService(httpClient);
test('UserService fetches users', async () => {
  const users = await userService.getUsers();
  expect(users.length).toBe(2);
});

Правильный подход: SOLID light

1. Начни с простого

// v1 - Просто работает
const UserList = () => {
  const [users, setUsers] = useState<User[]>([]);
  
  useEffect(() => {
    fetch('/api/users')
      .then(r => r.json())
      .then(setUsers);
  }, []);
  
  return <div>{users.map(u => <div>{u.name}</div>)}</div>;
};

2. Если усложняется - рефакторь

// v2 - Выделил hook
const useUsers = () => {
  const [users, setUsers] = useState<User[]>([]);
  useEffect(() => {
    fetch('/api/users').then(r => r.json()).then(setUsers);
  }, []);
  return users;
};

const UserList = () => {
  const users = useUsers();
  return <div>{users.map(u => <div>{u.name}</div>)}</div>;
};

3. Если нужна гибкость - добавь абстракцию

// v3 - API абстрактен
const useUsers = (apiClient = fetch) => {
  const [users, setUsers] = useState<User[]>([]);
  useEffect(() => {
    apiClient('/api/users').then(setUsers);
  }, [apiClient]);
  return users;
};

Заключение

Не нужно применять SOLID, если:

  • Проект маленький (< 20 компонентов)
  • Код не меняется часто
  • Реальной нет множественных реализаций
  • SOLID усложняет код больше, чем помогает

Нужно применять SOLID, если:

  • Проект большой и растет
  • Часто добавляют новые фичи
  • Реально есть несколько реализаций
  • SOLID упрощает кодовую базу

Правило: Начни с простого (KISS), затем рефакторь когда понадобится (TDD). SOLID должен служить гайдом, а не догме.

Хороший разработчик знает SOLID и применяет его когда это имеет смысл, а не всегда.