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

Зачем нужно разделять запросы и команды?

1.8 Middle🔥 121 комментариев
#Браузер и сетевые технологии

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

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

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

CQRS — Command Query Responsibility Segregation

CQRS — это архитектурный паттерн, который разделяет операции на две группы: команды (Commands) и запросы (Queries). Это разделение критично для построения масштабируемых и поддерживаемых приложений.

Разница между командами и запросами

Команды — операции, которые изменяют состояние:

  • Создание, обновление, удаление данных
  • Побочные эффекты
  • Возвращают минимум информации (успех/ошибку)

Запросы — операции, которые только читают данные:

  • Получение информации
  • Без побочных эффектов
  • Возвращают данные

Почему разделять?

1. Оптимизация производительности

// Запросы можно кэшировать
const users = cache.get("users") || fetchUsers();

// Команды всегда свежие
await updateUser(userId, data); // Нет кэша

2. Разные требования к масштабированию

  • Запросы: частые, много читателей → масштабируем читов
  • Команды: редкие, сложная бизнес-логика → ACID, транзакции

3. Независимая оптимизация

// Запросы: денормализованная БД для скорости
const getUsers = () => db.query(`SELECT id, name FROM users_view`);

// Команды: нормализованная БД для консистентности
const updateUser = async (id, data) => {
  await db.transaction(async (trx) => {
    await trx("users").where({id}).update(data);
    await trx("user_history").insert({user_id: id, ...});
  });
};

4. Определённые побочные эффекты

  • Запросы: без эффектов (идемпотентные)
  • Команды: явные эффекты (логирование, кэш-инвалидация)

5. Разное обращение с ошибками

// Запрос — не критично если старые данные
const data = await getUsers().catch(() => cache.fallback);

// Команда — обязательно успеть или откатить
await updateUser(id, data); // Без fallback

Пример в контексте фронтенда

// API структура

// Запросы (GET)
GET /api/v1/users              // Список пользователей
GET /api/v1/users/:id          // Один пользователь
GET /api/v1/questions/search   // Поиск вопросов

// Команды (POST/PUT/DELETE)
POST /api/v1/users             // Создать пользователя
PUT /api/v1/users/:id          // Обновить
DELETE /api/v1/users/:id       // Удалить

// В клиентском коде
const [users, setUsers] = useState([]);

// Запрос — можно кэшировать
const fetchUsers = useCallback(async () => {
  const cached = localStorage.getItem("users");
  if (cached) return JSON.parse(cached);
  
  const data = await fetch("/api/v1/users").then(r => r.json());
  localStorage.setItem("users", JSON.stringify(data));
  return data;
}, []);

// Команда — инвалидирует кэш
const createUser = useCallback(async (userData) => {
  const response = await fetch("/api/v1/users", {
    method: "POST",
    body: JSON.stringify(userData)
  });
  
  // Очищаем кэш после команды
  localStorage.removeItem("users");
  setUsers([]);
  
  return response.json();
}, []);

Практическое значение

Паттерн CQRS помогает предсказывать поведение, избежать багов с побочными эффектами и оптимизировать производительность. На собеседовании это показывает, что вы думаете о масштабируемости и архитектуре.