[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"question-mikroservisy-chto-takoe-cqrs":3},{"id":4,"slug":5,"topicId":6,"topicSlug":7,"topicName":8,"topicEmoji":9,"question":10,"answer":11,"codeLang":12,"codeSrc":12,"important":12,"commonMistakes":12,"modernUsage":12,"difficulty":13,"tags":14,"related":16,"progress":17,"seo":18},831,"chto-takoe-cqrs",23,"mikroservisy","Микросервисы","🔗","Что такое CQRS?","CQRS (Command Query Responsibility Segregation) — это паттерн, разделяющий модели чтения и записи данных. Команды (Commands) изменяют состояние, запросы (Queries) читают данные. Для каждой задачи используется своя оптимизированная модель.\n\n```\n                    ┌─────────────────┐\n                    │    API Gateway   │\n                    └───────┬─────────┘\n                     ┌──────┴──────┐\n                     │             │\n              ┌──────▼──┐   ┌─────▼─────┐\n              │ Command  │   │   Query   │\n              │  Model   │   │   Model   │\n              └──────┬───┘   └─────▲─────┘\n                     │             │\n              ┌──────▼───┐   ┌────┴──────┐\n              │ Write DB  │──►│ Read DB   │\n              │(PostgreSQL)│  │(Elasticsearch,│\n              └───────────┘  │ Redis, etc.)  │\n                             └──────────────┘\n```\n\n### Зачем нужен CQRS\n\n- Разная оптимизация — модель записи нормализована (3NF), модель чтения денормализована для быстрых запросов.\n- Масштабирование — чтений обычно гораздо больше, чем записей (соотношение 100:1). Можно масштабировать read-модель отдельно.\n- Разные хранилища — запись в PostgreSQL, чтение из Elasticsearch или Redis.\n- Естественно сочетается с Event Sourcing — события из write-модели проецируются в read-модель.\n\n\u003Cdetails>\u003Csummary>Пример реализации CQRS\u003C\u002Fsummary>\n\n```java\n\u002F\u002F Command — изменение данных\npublic record TransferMoneyCommand(\n    UUID fromAccountId,\n    UUID toAccountId,\n    BigDecimal amount\n) {}\n\n@Service\n@RequiredArgsConstructor\npublic class AccountCommandService {\n    private final AccountRepository accountRepository;\n    private final EventPublisher eventPublisher;\n\n    @Transactional\n    public void handle(TransferMoneyCommand cmd) {\n        Account from = accountRepository.findById(cmd.fromAccountId());\n        Account to = accountRepository.findById(cmd.toAccountId());\n\n        from.withdraw(cmd.amount());\n        to.deposit(cmd.amount());\n\n        accountRepository.save(from);\n        accountRepository.save(to);\n\n        eventPublisher.publish(new MoneyTransferredEvent(\n            cmd.fromAccountId(), cmd.toAccountId(), cmd.amount()));\n    }\n}\n\n\u002F\u002F Query — чтение данных из оптимизированной read-модели\npublic record AccountBalanceQuery(UUID accountId) {}\n\n@Service\n@RequiredArgsConstructor\npublic class AccountQueryService {\n    private final AccountReadRepository readRepository; \u002F\u002F Может быть Redis, ES\n\n    public AccountBalanceView handle(AccountBalanceQuery query) {\n        return readRepository.findById(query.accountId())\n            .orElseThrow(() -> new AccountNotFoundException(query.accountId()));\n    }\n}\n\n\u002F\u002F Read-модель (денормализованный view)\n@Document(indexName = \"account-balances\") \u002F\u002F Elasticsearch\npublic class AccountBalanceView {\n    private UUID accountId;\n    private String customerName;\n    private BigDecimal balance;\n    private String currency;\n    private LocalDateTime lastTransactionDate;\n    private int totalTransactions;\n}\n\n\u002F\u002F Проекция — обновление read-модели при получении событий\n@Service\npublic class AccountBalanceProjection {\n\n    @KafkaListener(topics = \"account-events\")\n    public void project(AccountEvent event) {\n        switch (event) {\n            case MoneyDeposited e -> readRepository.updateBalance(\n                e.accountId(), e.amount());\n            case MoneyWithdrawn e -> readRepository.updateBalance(\n                e.accountId(), e.amount().negate());\n        }\n    }\n}\n```\n\n\u003C\u002Fdetails>\n\nCQRS добавляет сложность. Используйте его только когда есть реальная необходимость:\n- Существенная разница в нагрузке чтение\u002Fзапись\n- Разные требования к моделям чтения и записи\n- Необходимость поддержки сложных запросов (полнотекстовый поиск, агрегации)\n\n> **На собеседовании:** объясните связку CQRS + Event Sourcing: события из write-модели проецируются в read-модель. Обязательно упомяните eventual consistency как следствие разделения моделей. Частая ошибка — предлагать CQRS для простых CRUD-приложений.","","senior",[15],"microservices",[],null,{"title":19,"description":20,"ogTitle":19,"ogDescription":21,"keywords":22,"schemaAnswer":23,"featuredSnippetReady":24},"Что такое CQRS? — Gymterview","CQRS (Command Query Responsibility Segregation) — это паттерн, разделяющий модели чтения и записи данных. Команды (Commands) изменяют состояние, запросы (Querie","CQRS (Command Query Responsibility Segregation) — это паттерн, разделяющий модели чтения и записи данных. Команды (Comma",[15,13],"CQRS (Command Query Responsibility Segregation) — это паттерн, разделяющий модели чтения и записи данных. Команды (Commands) изменяют состояние, запросы (Queries) читают данные. Для каждой задачи используется своя оптимизированная модель.",true]