[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"question-mikroservisy-chto-takoe-pattern-outbox":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},832,"chto-takoe-pattern-outbox",23,"mikroservisy","Микросервисы","🔗","Что такое паттерн Outbox?","Outbox (Transactional Outbox) — это паттерн, решающий проблему атомарности при необходимости одновременно обновить базу данных и опубликовать событие в брокер сообщений.\n\n### Проблема\n\nНельзя атомарно выполнить запись в БД и отправку в Kafka. Возможны ситуации:\n- Запись в БД прошла, отправка в Kafka — нет (потеря события).\n- Отправка в Kafka прошла, запись в БД — нет (фантомное событие).\n\n### Решение\n\nСобытие записывается в специальную таблицу `outbox` в той же транзакции, что и бизнес-данные. Отдельный процесс читает эту таблицу и публикует события в брокер.\n\n```\n┌─── Одна транзакция ───────────────┐\n│  1. UPDATE accounts SET balance... │\n│  2. INSERT INTO outbox (event...) │\n└───────────────────────────────────┘\n         │\n         ▼\n┌─── Отдельный процесс ────────────┐\n│  Читает outbox → Публикует в     │\n│  Kafka → Помечает как отправлено │\n└───────────────────────────────────┘\n```\n\n\u003Cdetails>\u003Csummary>Реализация Outbox паттерна\u003C\u002Fsummary>\n\n```java\n\u002F\u002F Таблица outbox\n\u002F\u002F CREATE TABLE outbox (\n\u002F\u002F   id UUID PRIMARY KEY,\n\u002F\u002F   aggregate_type VARCHAR(255),\n\u002F\u002F   aggregate_id VARCHAR(255),\n\u002F\u002F   event_type VARCHAR(255),\n\u002F\u002F   payload JSONB,\n\u002F\u002F   created_at TIMESTAMP DEFAULT now(),\n\u002F\u002F   sent BOOLEAN DEFAULT false\n\u002F\u002F );\n\n@Entity\n@Table(name = \"outbox\")\npublic class OutboxEvent {\n    @Id\n    private UUID id;\n    private String aggregateType;\n    private String aggregateId;\n    private String eventType;\n    @Column(columnDefinition = \"jsonb\")\n    private String payload;\n    private Instant createdAt;\n    private boolean sent;\n}\n\n\u002F\u002F Сервис, который атомарно сохраняет данные и событие\n@Service\n@RequiredArgsConstructor\npublic class PaymentService {\n    private final PaymentRepository paymentRepository;\n    private final OutboxRepository outboxRepository;\n\n    @Transactional \u002F\u002F Одна транзакция!\n    public void processPayment(PaymentRequest request) {\n        \u002F\u002F 1. Бизнес-логика\n        Payment payment = new Payment(request);\n        payment.setStatus(PaymentStatus.COMPLETED);\n        paymentRepository.save(payment);\n\n        \u002F\u002F 2. Сохраняем событие в outbox\n        OutboxEvent event = new OutboxEvent();\n        event.setId(UUID.randomUUID());\n        event.setAggregateType(\"Payment\");\n        event.setAggregateId(payment.getId().toString());\n        event.setEventType(\"PaymentCompleted\");\n        event.setPayload(objectMapper.writeValueAsString(\n            new PaymentCompletedEvent(payment.getId(), payment.getAmount())));\n        event.setCreatedAt(Instant.now());\n        outboxRepository.save(event);\n    }\n}\n\n\u002F\u002F Polling Publisher — читает outbox и отправляет в Kafka\n@Scheduled(fixedDelay = 1000)\n@Transactional\npublic void publishOutboxEvents() {\n    List\u003COutboxEvent> events = outboxRepository.findBySentFalseOrderByCreatedAt();\n    for (OutboxEvent event : events) {\n        kafkaTemplate.send(\"payment-events\", event.getAggregateId(), event.getPayload());\n        event.setSent(true);\n        outboxRepository.save(event);\n    }\n}\n```\n\n\u003C\u002Fdetails>\n\n### Альтернативный подход -- Debezium CDC (Change Data Capture)\n\nВместо polling используется Debezium, который отслеживает WAL (Write-Ahead Log) PostgreSQL и автоматически публикует записи из таблицы outbox в Kafka. Это более надёжно и эффективно:\n\n```json\n{\n  \"name\": \"outbox-connector\",\n  \"config\": {\n    \"connector.class\": \"io.debezium.connector.postgresql.PostgresConnector\",\n    \"database.hostname\": \"postgres\",\n    \"database.dbname\": \"payments\",\n    \"table.include.list\": \"public.outbox\",\n    \"transforms\": \"outbox\",\n    \"transforms.outbox.type\": \"io.debezium.transforms.outbox.EventRouter\"\n  }\n}\n```\n\n> **На собеседовании:** объясните проблему dual write (БД + брокер) и как Outbox её решает через единую транзакцию. Упомяните два способа публикации: polling и CDC (Debezium). Частая ошибка — не знать про Debezium как production-ready альтернативу polling.","","middle",[15],"microservices",[],null,{"title":19,"description":20,"ogTitle":19,"ogDescription":21,"keywords":22,"schemaAnswer":23,"featuredSnippetReady":24},"Что такое паттерн Sidecar? — Gymterview","Sidecar — это паттерн развёртывания, при котором вспомогательный контейнер работает рядом с основным сервисом в одном pod'е (в Kubernetes) и берёт на себя сквоз","Sidecar — это паттерн развёртывания, при котором вспомогательный контейнер работает рядом с основным сервисом в одном po",[15,13],"Sidecar — это паттерн развёртывания, при котором вспомогательный контейнер работает рядом с основным сервисом в одном pod'е (в Kubernetes) и берёт на себя сквозные задачи: проксирование трафика, логирование, мониторинг, безопасность.",true]