[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"question-rest-api-kak-proektirovat-api-dlya-mikroservisnoy-arkhitektury":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},1234,"kak-proektirovat-api-dlya-mikroservisnoy-arkhitektury",34,"rest-api","REST API","🌐","Как проектировать API для микросервисной архитектуры?","Проектирование API для микросервисов учитывает распределённую природу системы: сетевую ненадёжность, независимое развёртывание, согласованность данных и отказоустойчивость.\n\n### Синхронное взаимодействие (REST \u002F gRPC)\n\nОдин сервис напрямую вызывает другой и ждёт ответа.\n\n\u003Cdetails>\u003Csummary>Пример вызова через RestClient\u003C\u002Fsummary>\n\n```java\n@Service\n@RequiredArgsConstructor\npublic class OrderService {\n\n    private final RestClient userServiceClient;\n\n    public OrderDto createOrder(Long userId, CreateOrderRequest request) {\n        UserDto user = userServiceClient.get()\n            .uri(\"\u002Fapi\u002Fusers\u002F{id}\", userId)\n            .retrieve()\n            .body(UserDto.class);\n\n        if (user == null) {\n            throw new UserNotFoundException(\"User not found: \" + userId);\n        }\n\n        Order order = orderMapper.toEntity(request);\n        order.setUserId(userId);\n        order.setUserEmail(user.email());\n\n        return orderMapper.toDto(orderRepository.save(order));\n    }\n}\n\n@Configuration\npublic class RestClientConfig {\n\n    @Bean\n    public RestClient userServiceClient(RestClient.Builder builder) {\n        return builder\n            .baseUrl(\"http:\u002F\u002Fuser-service:8080\")\n            .defaultHeader(\"Content-Type\", \"application\u002Fjson\")\n            .requestInterceptor(new TokenRelayInterceptor())\n            .build();\n    }\n}\n```\n\n\u003C\u002Fdetails>\n\n### Асинхронное взаимодействие (Events \u002F Kafka)\n\nСервисы общаются через события — отправитель публикует событие, получатели подписаны на него.\n\n\u003Cdetails>\u003Csummary>Пример с Kafka\u003C\u002Fsummary>\n\n```java\n\u002F\u002F Публикация события\n@Service\n@RequiredArgsConstructor\npublic class OrderService {\n\n    private final KafkaTemplate\u003CString, OrderEvent> kafkaTemplate;\n\n    @Transactional\n    public OrderDto createOrder(CreateOrderRequest request) {\n        Order saved = orderRepository.save(orderMapper.toEntity(request));\n\n        OrderEvent event = new OrderEvent(\n            saved.getId(), \"ORDER_CREATED\",\n            saved.getUserId(), saved.getTotal(), Instant.now()\n        );\n        kafkaTemplate.send(\"order-events\", saved.getId().toString(), event);\n\n        return orderMapper.toDto(saved);\n    }\n}\n\n\u002F\u002F Обработка события в другом сервисе\n@Service\n@Slf4j\npublic class OrderEventListener {\n\n    @KafkaListener(topics = \"order-events\", groupId = \"notification-service\")\n    public void handleOrderEvent(OrderEvent event) {\n        if (\"ORDER_CREATED\".equals(event.eventType())) {\n            notificationService.sendOrderConfirmation(event.userId(), event.orderId());\n        }\n    }\n}\n```\n\n\u003C\u002Fdetails>\n\n### API Composition — агрегация данных из нескольких сервисов\n\n\u003Cdetails>\u003Csummary>Пример с Virtual Threads (Java 21+)\u003C\u002Fsummary>\n\n```java\n@RestController\n@RequestMapping(\"\u002Fapi\u002Fcustomer-view\")\n@RequiredArgsConstructor\npublic class CustomerViewController {\n\n    private final RestClient userServiceClient;\n    private final RestClient orderServiceClient;\n    private final RestClient loyaltyServiceClient;\n\n    @GetMapping(\"\u002F{userId}\")\n    public ResponseEntity\u003CCustomerView> getCustomerView(@PathVariable Long userId) {\n        try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {\n            var userTask = scope.fork(() ->\n                userServiceClient.get()\n                    .uri(\"\u002Fapi\u002Fusers\u002F{id}\", userId)\n                    .retrieve()\n                    .body(UserDto.class));\n\n            var ordersTask = scope.fork(() ->\n                orderServiceClient.get()\n                    .uri(\"\u002Fapi\u002Forders?userId={id}&limit=5\", userId)\n                    .retrieve()\n                    .body(new ParameterizedTypeReference\u003CList\u003COrderDto>>() {}));\n\n            var loyaltyTask = scope.fork(() ->\n                loyaltyServiceClient.get()\n                    .uri(\"\u002Fapi\u002Floyalty\u002F{id}\", userId)\n                    .retrieve()\n                    .body(LoyaltyDto.class));\n\n            scope.join().throwIfFailed();\n\n            return ResponseEntity.ok(new CustomerView(\n                userTask.get(), ordersTask.get(), loyaltyTask.get()\n            ));\n        }\n    }\n}\n```\n\n\u003C\u002Fdetails>\n\n### Circuit Breaker — защита от каскадных сбоев\n\n\u003Cdetails>\u003Csummary>Пример с Resilience4j\u003C\u002Fsummary>\n\n```java\n@Service\n@RequiredArgsConstructor\npublic class UserServiceClient {\n\n    private final RestClient restClient;\n\n    @CircuitBreaker(name = \"userService\", fallbackMethod = \"getUserFallback\")\n    @Retry(name = \"userService\")\n    @TimeLimiter(name = \"userService\")\n    public UserDto getUser(Long id) {\n        return restClient.get()\n            .uri(\"\u002Fapi\u002Fusers\u002F{id}\", id)\n            .retrieve()\n            .body(UserDto.class);\n    }\n\n    private UserDto getUserFallback(Long id, Throwable ex) {\n        log.warn(\"User service unavailable, returning fallback for user {}\", id, ex);\n        return new UserDto(id, \"Unknown User\", null);\n    }\n}\n```\n\n```yaml\nresilience4j:\n  circuitbreaker:\n    instances:\n      userService:\n        sliding-window-size: 10\n        failure-rate-threshold: 50\n        wait-duration-in-open-state: 10s\n  retry:\n    instances:\n      userService:\n        max-attempts: 3\n        wait-duration: 500ms\n        exponential-backoff-multiplier: 2\n  timelimiter:\n    instances:\n      userService:\n        timeout-duration: 3s\n```\n\n\u003C\u002Fdetails>\n\n### Saga Pattern — распределённые транзакции\n\nSaga — последовательность локальных транзакций, где каждый шаг имеет компенсирующее действие для отката.\n\n| Характеристика | Хореография (события) | Оркестрация (координатор) |\n|---------------|----------------------|--------------------------|\n| Связанность | Слабая (через события) | Средняя (координатор знает все шаги) |\n| Простота | Простые Saga | Сложные Saga с множеством шагов |\n| Отслеживание | Сложно (события распределены) | Просто (всё в координаторе) |\n| Единая точка отказа | Нет | Да (координатор) |\n\n\u003Cdetails>\u003Csummary>Примеры Saga\u003C\u002Fsummary>\n\nХореографическая Saga (через события):\n```java\n@Service\n@RequiredArgsConstructor\npublic class PaymentEventListener {\n\n    private final PaymentService paymentService;\n    private final KafkaTemplate\u003CString, PaymentEvent> kafkaTemplate;\n\n    @KafkaListener(topics = \"order-events\", groupId = \"payment-service\")\n    public void handleOrderCreated(OrderEvent event) {\n        if (!\"ORDER_CREATED\".equals(event.eventType())) return;\n\n        try {\n            paymentService.processPayment(event.orderId(), event.total());\n            kafkaTemplate.send(\"payment-events\",\n                new PaymentEvent(event.orderId(), \"PAYMENT_COMPLETED\"));\n        } catch (InsufficientFundsException e) {\n            kafkaTemplate.send(\"payment-events\",\n                new PaymentEvent(event.orderId(), \"PAYMENT_FAILED\"));\n        }\n    }\n}\n```\n\nОркестрационная Saga:\n```java\n@Service\n@RequiredArgsConstructor\n@Slf4j\npublic class OrderSagaOrchestrator {\n\n    private final PaymentServiceClient paymentClient;\n    private final StockServiceClient stockClient;\n    private final DeliveryServiceClient deliveryClient;\n\n    @Transactional\n    public OrderResult executeOrderSaga(Order order) {\n        try {\n            StockReservation reservation = stockClient.reserve(order.getItems());\n\n            PaymentResult payment;\n            try {\n                payment = paymentClient.charge(order.getUserId(), order.getTotal());\n            } catch (Exception e) {\n                stockClient.cancelReservation(reservation.getId());\n                throw e;\n            }\n\n            try {\n                deliveryClient.schedule(order.getId(), order.getDeliveryAddress());\n            } catch (Exception e) {\n                paymentClient.refund(payment.getId());\n                stockClient.cancelReservation(reservation.getId());\n                throw e;\n            }\n\n            order.setStatus(OrderStatus.CONFIRMED);\n            return OrderResult.success(order);\n\n        } catch (Exception e) {\n            log.error(\"Order saga failed for order {}\", order.getId(), e);\n            order.setStatus(OrderStatus.FAILED);\n            return OrderResult.failure(order, e.getMessage());\n        }\n    }\n}\n```\n\n\u003C\u002Fdetails>\n\n### Ключевые принципы\n\n- Каждый микросервис владеет своими данными (database per service).\n- Синхронная связь (REST\u002FgRPC) — для немедленного ответа. Асинхронная (Kafka) — для событий.\n- Circuit Breaker обязателен для всех синхронных вызовов.\n- Saga Pattern заменяет распределённые транзакции.\n- Все обработчики событий должны быть идемпотентными.\n\n### Частые ошибки\n\n- Общая база данных для нескольких сервисов — нарушает автономность.\n- Отсутствие Circuit Breaker — каскадный сбой всей системы.\n- Синхронные цепочки вызовов (A -> B -> C -> D) — увеличивают латентность.\n- Распределённые транзакции (2PC) — не масштабируются, используйте Saga.\n- Слишком мелкие сервисы (nano-services) — overhead превышает пользу.\n\n### Как используется в 2026\n\n- Spring Boot 3.x + Virtual Threads — синхронный код с производительностью реактивного.\n- Spring Modulith — модульный монолит как альтернатива для проектов среднего размера.\n- Testcontainers — стандарт для интеграционного тестирования.\n- OpenTelemetry — единый стандарт для distributed tracing, metrics и logging.\n- Service Mesh (Istio, Linkerd) берёт на себя retry, circuit breaker и mTLS.\n\n> **На собеседовании:** нужно показать знание обоих типов взаимодействия (синхронное и асинхронное), Circuit Breaker и Saga Pattern. Частая ошибка — не упомянуть принцип database per service или предлагать 2PC вместо Saga для распределённых транзакций.","","senior",[15],"rest",[],null,{"title":19,"description":20,"ogTitle":19,"ogDescription":21,"keywords":22,"schemaAnswer":23,"featuredSnippetReady":24},"Как проектировать API для микросервисной архитектуры? — Gymterview","Проектирование API для микросервисов учитывает распределённую природу системы: сетевую ненадёжность, независимое развёртывание, согласованность данных и отказоу","Проектирование API для микросервисов учитывает распределённую природу системы: сетевую ненадёжность, независимое развёрт",[15,13],"Проектирование API для микросервисов учитывает распределённую природу системы: сетевую ненадёжность, независимое развёртывание, согласованность данных и отказоустойчивость.",true]