Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
CQRS (Command Query Responsibility Segregation)
CQRS — это архитектурный паттерн, который разделяет операции на две категории: команды (записи) и запросы (чтение). Основная идея заключается в том, что модели для создания и обновления данных отличаются от моделей для чтения данных.
Основные принципы
Традиционный подход использует одну модель для всех операций. CQRS же предлагает:
- Command (Команда) — операция, которая изменяет состояние системы (CREATE, UPDATE, DELETE)
- Query (Запрос) — операция, которая только читает данные без побочных эффектов (SELECT)
- Разделение моделей — команды и запросы могут иметь разные структуры данных
Пример реализации на Python
from dataclasses import dataclass
from typing import List
from datetime import datetime
# Команды (write model)
@dataclass
class CreateUserCommand:
username: str
email: str
password: str
@dataclass
class UpdateUserCommand:
user_id: int
email: str
# Запросы (read model)
@dataclass
class GetUserQuery:
user_id: int
@dataclass
class SearchUsersQuery:
search_term: str
# Write-сторона (Command Handler)
class CommandHandler:
def __init__(self, database):
self.db = database
def handle_create_user(self, cmd: CreateUserCommand):
"""Обработка команды создания пользователя"""
# Хэшируем пароль, валидируем, сохраняем
hashed_password = self._hash_password(cmd.password)
user_id = self.db.insert(
table="users",
username=cmd.username,
email=cmd.email,
password_hash=hashed_password,
created_at=datetime.utcnow()
)
# Публикуем событие
self._publish_event("UserCreated", {"user_id": user_id})
return user_id
def _hash_password(self, password: str) -> str:
import hashlib
return hashlib.sha256(password.encode()).hexdigest()
def _publish_event(self, event_type: str, data: dict):
print(f"Event published: {event_type} -> {data}")
# Read-сторона (Query Handler)
class QueryHandler:
def __init__(self, read_database):
self.read_db = read_database
def handle_get_user(self, query: GetUserQuery):
"""Получение пользователя (оптимизировано для чтения)"""
# Может читать из кэша, репликированной БД или специального хранилища
return self.read_db.get_user_by_id(query.user_id)
def handle_search_users(self, query: SearchUsersQuery) -> List[dict]:
"""Поиск пользователей"""
# Может использовать Elasticsearch, специальный индекс и т.д.
return self.read_db.search_users(query.search_term)
# Применение в приложении
class UserService:
def __init__(self, command_handler, query_handler):
self.commands = command_handler
self.queries = query_handler
def create_user(self, username: str, email: str, password: str):
cmd = CreateUserCommand(
username=username,
email=email,
password=password
)
return self.commands.handle_create_user(cmd)
def get_user(self, user_id: int):
query = GetUserQuery(user_id=user_id)
return self.queries.handle_get_user(query)
def search_users(self, term: str):
query = SearchUsersQuery(search_term=term)
return self.queries.handle_search_users(query)
Преимущества CQRS
- Оптимизация производительности — read и write могут использовать разные технологии (PostgreSQL для записей, Redis для чтений)
- Независимое масштабирование — read-операции можно масштабировать отдельно от write
- Сложные запросы — легче реализовать сложные аналитические запросы на отдельной read-модели
- Разделение ответственности — код для команд и запросов не смешивается
- Event Sourcing — естественно интегрируется с event sourcing (история всех изменений)
Недостатки
- Повышенная сложность — требует больше кода и понимания архитектуры
- Консистентность данных — между write и read могут быть временные рассинхронизации (eventual consistency)
- Поддержка двух моделей — нужно обновлять и синхронизировать две модели данных
Когда использовать
CQRS особенно полезен в:
- Системах с высокой нагрузкой на чтение
- Приложениях с Event Sourcing
- Микросервисной архитектуре
- Системах с разными требованиями к read и write операциям
Для простых CRUD приложений CQRS часто является overengineering.