Ответ
В моей практике сервисы общаются через несколько основных механизмов, выбор которых зависит от требований к задержке, связности и надежности.
Основные паттерны взаимодействия:
| Способ | Протокол/Технология | Когда использовать | Пример сценария для QA |
|---|---|---|---|
| Синхронный запрос-ответ | REST/HTTP, gRPC, GraphQL | Когда нужен немедленный результат. | Тестирование API эндпоинтов (Postman, автотесты). Проверка корректности кодов ответа, схемы JSON, времени отклика. |
| Асинхронное обмен сообщениями | Kafka, RabbitMQ (AMQP), AWS SQS/SNS | Для декoupling, фоновых задач, событийной архитектуры. | Тестирование того, что событие опубликовано, доставлено и обработано. Проверка идемпотентности потребителей. |
| Совместная база данных | Любая СУБД (антипаттерн для МСА) | Стараться избегать. Иногда для быстрого прототипа. | Тестирование целостности данных и отсутствия прямых SQL-запросов между сервисами. |
Практический пример тестирования взаимодействия:
Допустим, у нас есть сервис оплаты, который по REST вызывает платёжный шлюз, а затем через Kafka отправляет событие PaymentProcessed.
- Тест REST-вызова: Мокаем внешний платёжный шлюз с помощью WireMock, чтобы симулировать успешные и ошибочные ответы.
- Тест Kafka-события: В интеграционном тесте проверяем, что после успешной оплаты в конкретный топик Kafka было отправлено сообщение с ожидаемой структурой. Для этого можно использовать embedded Kafka в тестах.
// Псевдокод интеграционного теста с Kafka @Test void shouldPublishPaymentProcessedEvent() { // 1. Вызываем API оплаты post("/api/payments", paymentRequest); // 2. Читаем сообщение из тестового топика Kafka ConsumerRecord<String, PaymentEvent> record = kafkaConsumer.poll(Duration.ofSeconds(5)).get(0); // 3. Assert assertThat(record.value().getStatus()).isEqualTo("SUCCESS"); }Ключевые нефункциональные аспекты для проверки: таймауты, retry-логика, обработка недоступности сервиса (circuit breaker).