[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"question-mikroservisy-chto-takoe-pattern-saga-i-kak-on-reshaet-problemu-raspredelyonnykh-tranzaktsiy":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},829,"chto-takoe-pattern-saga-i-kak-on-reshaet-problemu-raspredelyonnykh-tranzaktsiy",23,"mikroservisy","Микросервисы","🔗","Что такое паттерн Saga и как он решает проблему распределённых транзакций?","Saga — это паттерн управления распределёнными транзакциями, при котором длинная бизнес-транзакция разбивается на последовательность локальных транзакций в разных сервисах. При сбое выполняются компенсирующие транзакции для отмены уже выполненных шагов.\n\n> Аналогия из жизни: бронирование путешествия. Вы бронируете авиабилет, затем отель, затем экскурсию. Если экскурсию забронировать не удалось — вы отменяете отель и авиабилет (компенсирующие действия).\n\n### Проблема\n\nВ микросервисах нельзя использовать распределённые ACID-транзакции (2PC — Two-Phase Commit), так как они плохо масштабируются и создают сильную связность.\n\n### Два подхода к реализации\n\n| Критерий | Хореография | Оркестрация |\n|---|---|---|\n| Координация | Каждый сервис сам публикует\u002Fслушает события | Центральный оркестратор управляет шагами |\n| Связность | Сервисы слабо связаны | Оркестратор знает обо всех шагах |\n| Единая точка отказа | Нет | Оркестратор |\n| Видимость процесса | Сложно отследить | Легко |\n| Рекомендации | 3-4 шага | 5+ шагов |\n\n\u003Cdetails>\u003Csummary>Хореография (Choreography)\u003C\u002Fsummary>\n\n```java\n\u002F\u002F Сервис заявок — публикует событие\n@Service\npublic class ApplicationService {\n    @Transactional\n    public void createApplication(CreditApplication app) {\n        applicationRepository.save(app);\n        eventPublisher.publish(new ApplicationCreatedEvent(app.getId(), app.getCustomerId()));\n    }\n\n    \u002F\u002F Компенсация\n    @KafkaListener(topics = \"scoring-failed-events\")\n    public void handleScoringFailed(ScoringFailedEvent event) {\n        applicationRepository.updateStatus(event.getApplicationId(), Status.REJECTED);\n    }\n}\n\n\u002F\u002F Сервис скоринга — слушает событие и обрабатывает\n@Service\npublic class ScoringService {\n    @KafkaListener(topics = \"application-created-events\")\n    public void handleApplicationCreated(ApplicationCreatedEvent event) {\n        ScoringResult result = performScoring(event.getCustomerId());\n        if (result.isApproved()) {\n            eventPublisher.publish(new ScoringApprovedEvent(event.getApplicationId()));\n        } else {\n            eventPublisher.publish(new ScoringFailedEvent(event.getApplicationId()));\n        }\n    }\n}\n```\n\n\u003C\u002Fdetails>\n\n\u003Cdetails>\u003Csummary>Оркестрация (Orchestration)\u003C\u002Fsummary>\n\n```java\n\u002F\u002F Saga-оркестратор\n@Service\n@RequiredArgsConstructor\npublic class CreditSagaOrchestrator {\n    private final ApplicationClient applicationClient;\n    private final ScoringClient scoringClient;\n    private final AccountClient accountClient;\n\n    public void executeCreditSaga(CreditRequest request) {\n        SagaState state = new SagaState(request);\n        try {\n            \u002F\u002F Шаг 1: Создать заявку\n            state.setApplicationId(applicationClient.create(request));\n\n            \u002F\u002F Шаг 2: Скоринг\n            ScoringResult scoring = scoringClient.evaluate(request.getCustomerId());\n            if (!scoring.isApproved()) {\n                throw new ScoringRejectedException(scoring.getReason());\n            }\n\n            \u002F\u002F Шаг 3: Открыть счёт\n            state.setAccountId(accountClient.openCreditAccount(request));\n\n            \u002F\u002F Шаг 4: Перевести средства\n            paymentClient.transfer(state.getAccountId(), request.getAmount());\n\n        } catch (Exception e) {\n            compensate(state);\n            throw new SagaFailedException(\"Кредитная saga провалена\", e);\n        }\n    }\n\n    private void compensate(SagaState state) {\n        if (state.getAccountId() != null) {\n            accountClient.closeAccount(state.getAccountId());\n        }\n        if (state.getApplicationId() != null) {\n            applicationClient.reject(state.getApplicationId());\n        }\n    }\n}\n```\n\n\u003C\u002Fdetails>\n\n> **На собеседовании:** знайте оба подхода и когда какой выбрать. Обязательно упомяните компенсирующие транзакции — это ядро паттерна. Частая ошибка — путать Saga с 2PC (двухфазным коммитом).","","senior",[15],"microservices",[],null,{"title":19,"description":20,"ogTitle":19,"ogDescription":21,"keywords":22,"schemaAnswer":23,"featuredSnippetReady":24},"Что такое паттерн Saga и как он решает проблему распределённ — Gymterview","Saga — это паттерн управления распределёнными транзакциями, при котором длинная бизнес-транзакция разбивается на последовательность локальных транзакций в разны","Saga — это паттерн управления распределёнными транзакциями, при котором длинная бизнес-транзакция разбивается на последо",[15,13],"Saga — это паттерн управления распределёнными транзакциями, при котором длинная бизнес-транзакция разбивается на последовательность локальных транзакций в разных сервисах. При сбое выполняются компенсирующие транзакции для отмены уже выполненных шагов.",true]